Reputation: 4382
I've searched over this topic but found very few details which were helpful. With these details I've tried to cook some code as follows.
Note: Please compare the details shared in this post with other posts before marking this as DUPLICATE, and not just by the subject.
- (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];
}
This code collects information of internet usage of an iPhone device (and not my application alone).
Now, if I use internet through WiFi or through 3G, I get the the data (bytes) only in ifi_obytes
(sent) and ifi_ibytes
(received) but I think I should get WiFi usage in ifi_opackets
and ifi_ipackets
.
Also wanted to add that if I'm connected to a WiFi network, but am not using internet, I still get value added to ifi_obytes
and ifi_ibytes
.
May be I'm wrong in the implementation or understanding. Need someone to help me out.
Edit: Instead of AF_LINK
I tried AF_INET
(sockaddr_in
instead of sockaddr_dl
). This crashes the application.
Upvotes: 142
Views: 60570
Reputation: 635
Sorry for same answer again.
but I found that UInt32 is not enough, so it crashes when it became too big.
I just changed UInt32 to UInt64 and it works fine.
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
}
}
Upvotes: 0
Reputation: 531
To add to the accepted answer, its important to realize that the amount of data displayed by the interface overflows and restarts at 0
after every 4 GB
, especially if you are using this code to calculate the difference between two readings. This is because ifi_obytes
and ifi_ibytes
are uint_32
and their max value is 4294967295
.
Also, I recommend using unsigned int
s for the variables containing the data sent and received. Regular int
s have half the max value of an unsigned integer, so when adding ifi_obytes
, it may cause an overflow.
unsigned int sent = 0;
sent += networkStatisc->ifi_obytes;
Upvotes: 17
Reputation: 474
It's important to understand that these counters are provided since the device's last boot.
So, to make effective use of them, you should accompany every sample with the device's uptime (you can use mach_absolute_time()
- see this for more information)
Once you have counters samples + uptime you can have better heuristics as to data use...
Upvotes: 17
Reputation: 1983
The thing is that pdp_ip0
is one of interfaces, all pdpXXX
are WWAN
interfaces dedicated to different functions, voicemail, general networking interface.
I read in Apple forum that : The OS does not keep network statistics on a process-by-process basis. As such, there's no exact solution to this problem. You can, however, get network statistics for each network interface.
In general en0
is your Wi-Fi
interface and pdp_ip0
is your WWAN
interface.
There is no good way to get information wifi/cellular network data since, particular date-time!
Data statistic (ifa_data->ifi_obytes
and ifa_data->ifi_ibytes
) are stored from previous device reboot.
I don't know why, but ifi_opackets
and ifi_ipackets
are shown just for lo0
(I think its main interface ).
Yes. Then device is connected via WiFi
and doesn't use internet if_iobytes
values still come because this method provides network bytes exchanges and not just internet.
#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)};
}
Improved copy/paste support !
Upvotes: 179
Reputation: 1310
A new version about based on previous versions, but adapted for Swift4 and 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
}
}
Upvotes: 4
Reputation: 1275
I fixed above source code to Swift3 version
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
}
}
Upvotes: 4
Reputation: 14477
Swift version of the accepted answer. I also break the code into smaller units.
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
}
}
Upvotes: 5