10年世界杯冠军_梅西2018年世界杯 - kefulq.com

iOS流量监测

直播女篮世界杯 2025-06-01 04:28:50

背景

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

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

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

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

几种具体流量监测方案

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

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

首先需要引入这几个系统库

#include

#include

#include

#include

/**

* 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.本地统计各网络接口流量

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

针对URLConnection、CFNetwork、NSURLSession三种网络做Hook,hook的具体技术可以是method swizzle 也可以是Proxy

也可以使用 NSURLProtocol 对网络请求的拦截,进而得到流量、响应时间等信息,但是NSURLProtocol有自己的局限,比如NSURLProtocol只能拦截NSURLSession,NSURLConnection以及UIWebView,但是对于CFNetwork则无能为力

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

1.针对URLConnection、CFNetwork、NSURLSession三种网络做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