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