IPhone 数据使用跟踪/监控

我搜索了这个主题,但是发现很少有有用的细节。通过这些细节,我尝试编写一些代码,如下所示。

注意: 在标记为副本之前,请将本文中分享的细节与其他文章进行比较,而不仅仅是按主题分享。

- (NSArray *)getDataCountersForType:(int)type {
BOOL success;
struct ifaddrs *addrs = nil;
const struct ifaddrs *cursor = nil;
const struct sockaddr_dl *dlAddr = nil;
const struct if_data *networkStatisc = nil;


int dataSent = 0;
int dataReceived = 0;


success = getifaddrs(&addrs) == 0;
if (success) {
cursor = addrs;
while (cursor != NULL) {
if (cursor->ifa_addr->sa_family == AF_LINK) {
dlAddr = (const struct sockaddr_dl *) cursor->ifa_addr;
networkStatisc = (const struct if_data *) cursor->ifa_data;


if (type == WiFi) {
dataSent += networkStatisc->ifi_opackets;
dataReceived += networkStatisc->ifi_ipackets;
}
else if (type == WWAN) {
dataSent += networkStatisc->ifi_obytes;
dataReceived += networkStatisc->ifi_ibytes;
}
}
cursor = cursor->ifa_next;
}
freeifaddrs(addrs);
}
return [NSArray arrayWithObjects:[NSNumber numberWithInt:dataSent], [NSNumber numberWithInt:dataReceived], nil];
}

这段代码收集 iPhone 设备的互联网使用信息(不仅仅是我的应用程序)。

现在,如果我通过 WiFi 或3G 使用互联网,我只能得到 ifi_obytes(发送)和 ifi_ibytes(接收)中的数据(字节) ,但我认为我应该得到 ifi_opacketsifi_ipackets中的 WiFi 使用情况。

还想补充的是,如果我连接到一个 WiFi 网络,但不使用互联网,我仍然得到增值的 ifi_obytesifi_ibytes

可能是我在执行或理解上有错误。需要有人帮我解决。


编辑: 我尝试用 AF_INET(sockaddr_in而不是 sockaddr_dl)代替 AF_LINK,这会使应用程序崩溃。

58486 次浏览

问题是,pdp_ip0是接口之一,所有的 pdpXXX都是 WWAN接口,专用于不同的功能,语音邮件,通用网络接口。

我在苹果论坛上看到: 操作系统不保存逐个进程的网络统计数据。因此,这个问题没有确切的解决方案。但是,您可以获取每个网络接口的网络统计信息。

一般来说,en0Wi-Fi接口,pdp_ip0WWAN接口。

没有好的方法来获取信息 wifi/蜂窝网络数据,因为,特定的日期时间!

数据统计信息(ifa_data->ifi_obytesifa_data->ifi_ibytes)存储在以前的设备重启中。

我不知道为什么,但 ifi_opacketsifi_ipackets显示只为 lo0(我认为它的主要接口)。

是的。然后设备通过 WiFi连接,不使用因特网 if_iobytes值仍然来,因为这种方法提供网络字节交换,而不仅仅是因特网。

#include <net/if.h>
#include <ifaddrs.h>


static NSString *const DataCounterKeyWWANSent = @"WWANSent";
static NSString *const DataCounterKeyWWANReceived = @"WWANReceived";
static NSString *const DataCounterKeyWiFiSent = @"WiFiSent";
static NSString *const DataCounterKeyWiFiReceived = @"WiFiReceived";


NSDictionary *DataCounters()
{
struct ifaddrs *addrs;
const struct ifaddrs *cursor;


u_int32_t WiFiSent = 0;
u_int32_t WiFiReceived = 0;
u_int32_t WWANSent = 0;
u_int32_t WWANReceived = 0;


if (getifaddrs(&addrs) == 0)
{
cursor = addrs;
while (cursor != NULL)
{
if (cursor->ifa_addr->sa_family == AF_LINK)
{
#ifdef DEBUG
const struct if_data *ifa_data = (struct if_data *)cursor->ifa_data;
if (ifa_data != NULL)
{
NSLog(@"Interface name %s: sent %tu received %tu",cursor->ifa_name,ifa_data->ifi_obytes,ifa_data->ifi_ibytes);
}
#endif


// name of interfaces:
// en0 is WiFi
// pdp_ip0 is WWAN
NSString *name = @(cursor->ifa_name);
if ([name hasPrefix:@"en"])
{
const struct if_data *ifa_data = (struct if_data *)cursor->ifa_data;
if (ifa_data != NULL)
{
WiFiSent += ifa_data->ifi_obytes;
WiFiReceived += ifa_data->ifi_ibytes;
}
}


if ([name hasPrefix:@"pdp_ip"])
{
const struct if_data *ifa_data = (struct if_data *)cursor->ifa_data;
if (ifa_data != NULL)
{
WWANSent += ifa_data->ifi_obytes;
WWANReceived += ifa_data->ifi_ibytes;
}
}
}


cursor = cursor->ifa_next;
}


freeifaddrs(addrs);
}


return @{DataCounterKeyWiFiSent : @(WiFiSent),
DataCounterKeyWiFiReceived : @(WiFiReceived),
DataCounterKeyWWANSent : @(WWANSent),
DataCounterKeyWWANReceived : @(WWANReceived)};
}

改进的复制/粘贴支持!

了解这些计数器提供的是 从设备最后一次启动开始。非常重要

因此,为了有效地利用它们,你应该在设备的正常运行时间附上每个样品(你可以使用 mach_absolute_time()-参见 这个了解更多信息)

一旦你有计数器样本 + 正常运行时间,你可以有更好的启发式数据使用..。

为了补充已接受的答案,重要的是要认识到接口显示的数据量溢出,并在每次 4 GB之后在 0重新启动,特别是如果您使用此代码来计算两个读数之间的差异。这是因为 ifi_obytesifi_ibytesuint_32,它们的最大值是 4294967295

另外,我建议对包含发送和接收的数据的变量使用 unsigned int。常规 int的最大值只有无符号整数的一半,因此在添加 ifi_obytes时,可能会导致溢出。

unsigned int sent = 0;
sent += networkStatisc->ifi_obytes;

快速版本的接受的答案。我也打破了代码成更小的单位。

struct DataUsageInfo {
var wifiReceived: UInt32 = 0
var wifiSent: UInt32 = 0
var wirelessWanDataReceived: UInt32 = 0
var wirelessWanDataSent: UInt32 = 0


mutating func updateInfoByAdding(info: DataUsageInfo) {
wifiSent += info.wifiSent
wifiReceived += info.wifiReceived
wirelessWanDataSent += info.wirelessWanDataSent
wirelessWanDataReceived += info.wirelessWanDataReceived
}
}


class DataUsage {


private static let wwanInterfacePrefix = "pdp_ip"
private static let wifiInterfacePrefix = "en"


class func getDataUsage() -> DataUsageInfo {
var interfaceAddresses: UnsafeMutablePointer<ifaddrs> = nil
var dataUsageInfo = DataUsageInfo()


guard getifaddrs(&interfaceAddresses) == 0 else { return dataUsageInfo }


var pointer = interfaceAddresses
while pointer != nil {
guard let info = getDataUsageInfo(from: pointer) else {
pointer = pointer.memory.ifa_next
continue
}
dataUsageInfo.updateInfoByAdding(info)
pointer = pointer.memory.ifa_next
}


freeifaddrs(interfaceAddresses)


return dataUsageInfo
}


private class func getDataUsageInfo(from infoPointer: UnsafeMutablePointer<ifaddrs>) -> DataUsageInfo? {
let pointer = infoPointer


let name: String! = String.fromCString(infoPointer.memory.ifa_name)


let addr = pointer.memory.ifa_addr.memory
guard addr.sa_family == UInt8(AF_LINK) else { return nil }


return dataUsageInfo(from: pointer, name: name)
}


private class func dataUsageInfo(from pointer: UnsafeMutablePointer<ifaddrs>, name: String) -> DataUsageInfo {
var networkData: UnsafeMutablePointer<if_data> = nil
var dataUsageInfo = DataUsageInfo()


if name.hasPrefix(wifiInterfacePrefix) {
networkData = unsafeBitCast(pointer.memory.ifa_data, UnsafeMutablePointer<if_data>.self)
dataUsageInfo.wifiSent += networkData.memory.ifi_obytes
dataUsageInfo.wifiReceived += networkData.memory.ifi_ibytes
} else if name.hasPrefix(wwanInterfacePrefix) {
networkData = unsafeBitCast(pointer.memory.ifa_data, UnsafeMutablePointer<if_data>.self)
dataUsageInfo.wirelessWanDataSent += networkData.memory.ifi_obytes
dataUsageInfo.wirelessWanDataReceived += networkData.memory.ifi_ibytes
}


return dataUsageInfo
}
}

我把上面的源代码改成了 Swift3版本

struct DataUsageInfo {
var wifiReceived: UInt32 = 0
var wifiSent: UInt32 = 0
var wirelessWanDataReceived: UInt32 = 0
var wirelessWanDataSent: UInt32 = 0


mutating func updateInfoByAdding(_ info: DataUsageInfo) {
wifiSent += info.wifiSent
wifiReceived += info.wifiReceived
wirelessWanDataSent += info.wirelessWanDataSent
wirelessWanDataReceived += info.wirelessWanDataReceived
}
}




class DataUsage {


private static let wwanInterfacePrefix = "pdp_ip"
private static let wifiInterfacePrefix = "en"


class func getDataUsage() -> DataUsageInfo {
var ifaddr: UnsafeMutablePointer<ifaddrs>?
var dataUsageInfo = DataUsageInfo()


guard getifaddrs(&ifaddr) == 0 else { return dataUsageInfo }
while let addr = ifaddr {
guard let info = getDataUsageInfo(from: addr) else {
ifaddr = addr.pointee.ifa_next
continue
}
dataUsageInfo.updateInfoByAdding(info)
ifaddr = addr.pointee.ifa_next
}


freeifaddrs(ifaddr)


return dataUsageInfo
}


private class func getDataUsageInfo(from infoPointer: UnsafeMutablePointer<ifaddrs>) -> DataUsageInfo? {
let pointer = infoPointer
let name: String! = String(cString: pointer.pointee.ifa_name)
let addr = pointer.pointee.ifa_addr.pointee
guard addr.sa_family == UInt8(AF_LINK) else { return nil }


return dataUsageInfo(from: pointer, name: name)
}


private class func dataUsageInfo(from pointer: UnsafeMutablePointer<ifaddrs>, name: String) -> DataUsageInfo {
var networkData: UnsafeMutablePointer<if_data>?
var dataUsageInfo = DataUsageInfo()


if name.hasPrefix(wifiInterfacePrefix) {
networkData = unsafeBitCast(pointer.pointee.ifa_data, to: UnsafeMutablePointer<if_data>.self)
if let data = networkData {
dataUsageInfo.wifiSent += data.pointee.ifi_obytes
dataUsageInfo.wifiReceived += data.pointee.ifi_ibytes
}


} else if name.hasPrefix(wwanInterfacePrefix) {
networkData = unsafeBitCast(pointer.pointee.ifa_data, to: UnsafeMutablePointer<if_data>.self)
if let data = networkData {
dataUsageInfo.wirelessWanDataSent += data.pointee.ifi_obytes
dataUsageInfo.wirelessWanDataReceived += data.pointee.ifi_ibytes
}
}


return dataUsageInfo
}
}

新版本大约基于以前的版本,但适用于 Swift4和 Xcode 9

struct DataUsageInfo {
var wifiReceived: UInt32 = 0
var wifiSent: UInt32 = 0
var wirelessWanDataReceived: UInt32 = 0
var wirelessWanDataSent: UInt32 = 0


mutating func updateInfoByAdding(info: DataUsageInfo) {
wifiSent += info.wifiSent
wifiReceived += info.wifiReceived
wirelessWanDataSent += info.wirelessWanDataSent
wirelessWanDataReceived += info.wirelessWanDataReceived
}
}


class DataUsage {


private static let wwanInterfacePrefix = "pdp_ip"
private static let wifiInterfacePrefix = "en"


class func getDataUsage() -> DataUsageInfo {
var interfaceAddresses: UnsafeMutablePointer<ifaddrs>? = nil


var dataUsageInfo = DataUsageInfo()


guard getifaddrs(&interfaceAddresses) == 0 else { return dataUsageInfo }


var pointer = interfaceAddresses
while pointer != nil {
guard let info = getDataUsageInfo(from: pointer!) else {
pointer = pointer!.pointee.ifa_next
continue
}
dataUsageInfo.updateInfoByAdding(info: info)
pointer = pointer!.pointee.ifa_next
}


freeifaddrs(interfaceAddresses)


return dataUsageInfo
}


private class func getDataUsageInfo(from infoPointer: UnsafeMutablePointer<ifaddrs>) -> DataUsageInfo? {
let pointer = infoPointer


let name: String! = String(cString: infoPointer.pointee.ifa_name)
let addr = pointer.pointee.ifa_addr.pointee
guard addr.sa_family == UInt8(AF_LINK) else { return nil }


return dataUsageInfo(from: pointer, name: name)
}


private class func dataUsageInfo(from pointer: UnsafeMutablePointer<ifaddrs>, name: String) -> DataUsageInfo {
var networkData: UnsafeMutablePointer<if_data>? = nil
var dataUsageInfo = DataUsageInfo()


if name.hasPrefix(wifiInterfacePrefix) {
networkData = unsafeBitCast(pointer.pointee.ifa_data, to: UnsafeMutablePointer<if_data>.self)
dataUsageInfo.wifiSent += networkData?.pointee.ifi_obytes ?? 0
dataUsageInfo.wifiReceived += networkData?.pointee.ifi_ibytes ?? 0
} else if name.hasPrefix(wwanInterfacePrefix) {
networkData = unsafeBitCast(pointer.pointee.ifa_data, to: UnsafeMutablePointer<if_data>.self)
dataUsageInfo.wirelessWanDataSent += networkData?.pointee.ifi_obytes ?? 0
dataUsageInfo.wirelessWanDataReceived += networkData?.pointee.ifi_ibytes ?? 0
}


return dataUsageInfo
}
}

抱歉又是这个答案。

但是我发现 UInt32不够用,所以当它变得太大时会崩溃。

我刚刚把 UInt32改成了 UInt64,它工作得很好。

struct DataUsageInfo {
var wifiReceived: UInt64 = 0
var wifiSent: UInt64 = 0
var wirelessWanDataReceived: UInt64 = 0
var wirelessWanDataSent: UInt64 = 0


mutating func updateInfoByAdding(info: DataUsageInfo) {
wifiSent += info.wifiSent
wifiReceived += info.wifiReceived
wirelessWanDataSent += info.wirelessWanDataSent
wirelessWanDataReceived += info.wirelessWanDataReceived
}
}


class DataUsage {


private static let wwanInterfacePrefix = "pdp_ip"
private static let wifiInterfacePrefix = "en"


class func getDataUsage() -> DataUsageInfo {
var interfaceAddresses: UnsafeMutablePointer<ifaddrs>? = nil


var dataUsageInfo = DataUsageInfo()


guard getifaddrs(&interfaceAddresses) == 0 else { return dataUsageInfo }


var pointer = interfaceAddresses
while pointer != nil {
guard let info = getDataUsageInfo(from: pointer!) else {
pointer = pointer!.pointee.ifa_next
continue
}
dataUsageInfo.updateInfoByAdding(info: info)
pointer = pointer!.pointee.ifa_next
}


freeifaddrs(interfaceAddresses)


return dataUsageInfo
}


private class func getDataUsageInfo(from infoPointer: UnsafeMutablePointer<ifaddrs>) -> DataUsageInfo? {
let pointer = infoPointer


let name: String! = String(cString: infoPointer.pointee.ifa_name)
let addr = pointer.pointee.ifa_addr.pointee
guard addr.sa_family == UInt8(AF_LINK) else { return nil }


return dataUsageInfo(from: pointer, name: name)
}


private class func dataUsageInfo(from pointer: UnsafeMutablePointer<ifaddrs>, name: String) -> DataUsageInfo {
var networkData: UnsafeMutablePointer<if_data>? = nil
var dataUsageInfo = DataUsageInfo()


if name.hasPrefix(wifiInterfacePrefix) {
networkData = unsafeBitCast(pointer.pointee.ifa_data, to: UnsafeMutablePointer<if_data>.self)
dataUsageInfo.wifiSent += UInt64(networkData?.pointee.ifi_obytes ?? 0)
dataUsageInfo.wifiReceived += UInt64(networkData?.pointee.ifi_ibytes ?? 0)
} else if name.hasPrefix(wwanInterfacePrefix) {
networkData = unsafeBitCast(pointer.pointee.ifa_data, to: UnsafeMutablePointer<if_data>.self)
dataUsageInfo.wirelessWanDataSent += UInt64(networkData?.pointee.ifi_obytes ?? 0)
dataUsageInfo.wirelessWanDataReceived += UInt64(networkData?.pointee.ifi_ibytes ?? 0)
}


return dataUsageInfo
}
}