iOS流量监测

作者: yydev | 来源:发表于2017-05-24 16:31 被阅读1364次

背景

开发一款app,我们必须关注这款应用的性能问题,这自然就涉及到一个词APM。

APM 的全称是Application performance management,即应用性能管理,通过对应用的可靠性、稳定性等方面的监控,进而达到可以快速修复问题、提高用户体验的目的。

国内各大公司都有自己的一套监控体系,这个系统可能是自己研发,也可能是第三方提供,当然对于这个数据为王的时代,很多有实力的公司倾向于自主研发,掌握核心数据。比较有代表性的 APM 产品有:听云、阿里百川、腾讯 bugly、NewRelic、OneAPM、网易云捕等

本文只对流量监测这一块进行着重研究。

几种具体流量监测方案

1.统计设备流量总量的方案

参考stackoverflow一篇关于流量统计的问题,得到以下demo, 可以分别获取WiFi 和 WWAN网络环境下上行和下行流量总数。

首先需要引入这几个系统库
#include <arpa/inet.h>
#include <net/if.h>
#include <ifaddrs.h>
#include <net/if_dl.h>

/**
 *  WiFiSent WiFi发送流量
 *  WiFiReceived WiFi接收流量
 *  WWANSent 移动网络发送流量
 *  WWANReceived 移动网络接收流量
 */
+ (NSDictionary *)getTrafficMonitorings {
    NSDictionary * trafficDict = [[NSDictionary alloc] init];
    BOOL success;
    struct ifaddrs *addrs;
    const struct ifaddrs *cursor;
    const struct if_data *networkStatisc;
    int WiFiSent = 0;
    int WiFiReceived = 0;
    int WWANSent = 0;
    int WWANReceived = 0;
    NSString *name=[[NSString alloc]init];
    success = getifaddrs(&addrs) == 0;
    if (success) {
        cursor = addrs;
        while (cursor != NULL) {
            name=[NSString stringWithFormat:@"%s",cursor->ifa_name];

            if (cursor->ifa_addr->sa_family == AF_LINK) {
                //wifi消耗流量
                if ([name hasPrefix:@"en"]) {
                    networkStatisc = (const struct if_data *) cursor->ifa_data;
                    WiFiSent+=networkStatisc->ifi_obytes;
                    WiFiReceived+=networkStatisc->ifi_ibytes;
                }

                //移动网络消耗流量
                if ([name hasPrefix:@"pdp_ip0"]) {
                    networkStatisc = (const struct if_data *) cursor->ifa_data;
                    WWANSent+=networkStatisc->ifi_obytes;
                    WWANReceived+=networkStatisc->ifi_ibytes;
                }
            }
            cursor = cursor->ifa_next;
        }
        freeifaddrs(addrs);
    }
    NSString *WiFiSentTraffic = [NSString stringWithFormat:@"%d",WiFiSent];
    NSString *WiFiReceivedTraffic = [NSString stringWithFormat:@"%d",WiFiReceived];
    NSString *WiFiTotalTraffic = [NSString stringWithFormat:@"%d",WiFiSent + WiFiReceived];
    NSString *WWANSentTraffic = [NSString stringWithFormat:@"%d",WWANSent];
    NSString *WWANReceivedTraffic = [NSString stringWithFormat:@"%d",WWANReceived];
    NSString *WWANTotalTraffic = [NSString stringWithFormat:@"%d",WWANSent+WWANReceived];
    trafficDict = @{
                    @"WiFiSentTraffic":WiFiSentTraffic,
                    @"WiFiReceivedTraffic":WiFiReceivedTraffic,
                    @"WiFiTotalTraffic":WiFiTotalTraffic,
                    @"WWANSentTraffic":WWANSentTraffic,
                    @"WWANReceivedTraffic":WWANReceivedTraffic,
                    @"WWANTotalTraffic":WWANTotalTraffic
                    };

    return trafficDict;
}

这个方案有几个问题需要注意:

  • iOS是无法像Android一样监听到开机广播和关机广播的,立刻关机再开机,上面的方式监测的移动网络下流量会默认清空。
  • 应用在后台运行时,突然被杀死,无法对流量统计,需要在应用进入后台时先保存流量使用情况

2.本地统计各网络接口流量

本地统计流量,主要通过两种方式来做:

  • 针对URLConnectionCFNetworkNSURLSession三种网络做Hook,hook的具体技术可以是method swizzle 也可以是Proxy
  • 也可以使用 NSURLProtocol 对网络请求的拦截,进而得到流量、响应时间等信息,但是NSURLProtocol有自己的局限,比如NSURLProtocol只能拦截NSURLSession,NSURLConnection以及UIWebView,但是对于CFNetwork则无能为力

由于以上两种方式的局限性,需要将两种方式结合起来:

1.针对URLConnectionCFNetworkNSURLSession三种网络做Hook。

具体来说就是将流量监测代码插入系统方法实现,method swizzling可以达到这个效果。例如,对于网络请求开始,我们通过监测-[NSURLConnection start]方法就可以统计这一次请求的数据大小。

通过这种方式,可以监控指定类的指定方法,我们可以取得方法调用的时机, 但是程序中除了方法调用还存在方法回调,这是不适合用这种方式监控的情况。

例如NSURLConnection的构造方法和start方法可以通过Method Swizzling监控到, 但是回调消息的接收者delegate的类名不固定,可能是任意一个页面实例, 如果还要使用Method Swizzling的方法来监控,会面对未知个数的页面的delegate方法,不是一个好办法。

NSURLConnection - hook.jpg CFNetwork - hook.jpg

解决方法是构造一个回调消息的转发者作为代理,在转发者中收集数据,再转发给用户。

2.NSURLProtocol拦截是监控UIWebView请求最普遍的解决方案。具体可以参考美团技术团队的实现。

参考资料

移动端监控体系之技术原理剖析

网易NeteaseAPM iOS SDK技术实现分享

移动端性能监控方案Hertz

网络流量统计--NetworkTraffic

相关文章

网友评论

  • 心语风尚:这个代码用的系统库检测的是哪种网络的流量

本文标题:iOS流量监测

本文链接:https://www.haomeiwen.com/subject/zedoxxtx.html