Śledzenie / monitorowanie wykorzystania danych iPhone'a

136

Przeszukałem ten temat, ale znalazłem bardzo niewiele informacji, które były pomocne. Z tymi szczegółami próbowałem ugotować kod w następujący sposób.

Uwaga: przed oznaczeniem go jako DUPLIKAT porównaj szczegóły udostępnione w tym poście z innymi postami, a nie tylko temat.

- (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];    
}

Ten kod zbiera informacje o korzystaniu z Internetu przez urządzenie iPhone (a nie tylko o mojej aplikacji).

Teraz, jeśli korzystam z Internetu przez Wi-Fi lub 3G, otrzymuję dane (bajty) tylko w ifi_obytes(wysłane) i ifi_ibytes(odebrane), ale myślę, że powinienem uzyskać korzystanie z WiFi w ifi_opacketsi ifi_ipackets.

Chciałem też dodać, że jeśli jestem podłączony do sieci Wi-Fi, ale nie korzystam z Internetu, nadal otrzymuję wartość dodaną ifi_obytesi ifi_ibytes.

Może się mylę w implementacji lub rozumieniu Potrzebujesz kogoś, kto mi pomoże.


Edycja: Zamiast AF_LINKpróbowałem AF_INET( sockaddr_inzamiast sockaddr_dl). To powoduje awarię aplikacji.

Sahil Khanna
źródło

Odpowiedzi:

175

Chodzi o to, że pdp_ip0jest to jeden z interfejsów, wszystkie pdpXXXWWANinterfejsami dedykowanymi do różnych funkcji, poczty głosowej, ogólnego interfejsu sieciowego.

Czytałem na forum Apple, że: System operacyjny nie prowadzi statystyk sieciowych na podstawie procesu po procesie. W związku z tym nie ma dokładnego rozwiązania tego problemu. Możesz jednak uzyskać statystyki sieciowe dla każdego interfejsu sieciowego.

Ogólnie en0jest to twój Wi-Fiinterfejs i pdp_ip0jest to twój WWANinterfejs.

Nie ma dobrego sposobu na uzyskanie informacji o danych sieci Wi-Fi / komórkowej, ponieważ od określonej daty i godziny!

Statystyki danych ( ifa_data->ifi_obytesi ifa_data->ifi_ibytes) są przechowywane od poprzedniego ponownego uruchomienia urządzenia.

Nie wiem dlaczego, ale ifi_opacketsi ifi_ipacketssą wyświetlane tylko dla lo0(myślę, że jego główny interfejs).

Tak. Następnie urządzenie jest połączone przez WiFii nie używa if_iobyteswartości internetowych, ponieważ ta metoda zapewnia wymianę bajtów sieciowych, a nie tylko 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)};
}

Ulepszona obsługa kopiowania / wklejania!

user982705
źródło
1
Wielkie dzięki. Ten kod zdziałał cuda. Dziękuję również za wyjaśnienie sprawy.
Sahil Khanna
20
Musisz zaimportować te biblioteki: #include <arpa / inet.h> #include <net / if.h> #include <ifaddrs.h> #include <net / if_dl.h>
user982705,
9
Czy za pomocą tego kodu można śledzić ruch danych w aplikacji na iOS?
fvisticot
2
@Mat: Rozwiązanie, które zaprojektowałem, jeśli jest przeznaczone dla klientów korporacyjnych i nie jest wdrożone w AppStore.
Sahil Khanna
4
Czy ktoś wymyślił, jak uzyskać dane na aplikację? Udostępnij kod :)
Bob de Graaf
16

Ważne jest, aby zrozumieć, że liczniki te są dostarczane od ostatniego uruchomienia urządzenia.

Aby więc efektywnie je wykorzystać, do każdej próbki należy dołączyć czas pracy urządzenia (możesz użyć mach_absolute_time()- zobacz to po więcej informacji)

Gdy masz już próbki liczników + czas pracy, możesz mieć lepszą heurystykę w zakresie wykorzystania danych ...

Czarodziej
źródło
mach_absolute_time nie jest uptime. Jest to mniej więcej czas aktywności procesora. mach_absolute_time przeważnie przestaje liczyć, gdy urządzenie śpi.
Bob Whiteman
13

Aby dodać do zaakceptowanej odpowiedzi, ważne jest, aby zdać sobie sprawę, że ilość danych wyświetlanych przez interfejs przepełnia się i uruchamia się ponownie 0po każdym 4 GB, zwłaszcza jeśli używasz tego kodu do obliczenia różnicy między dwoma odczytami. Dzieje się tak, ponieważ ifi_obytesi ifi_ibytessą, uint_32a ich maksymalna wartość to 4294967295.

Polecam również używanie unsigned ints dla zmiennych zawierających wysłane i odebrane dane. Zwykłe ints mają połowę maksymalnej wartości liczby całkowitej bez znaku, więc podczas dodawania ifi_obytesmoże spowodować przepełnienie.

unsigned int sent = 0;
sent += networkStatisc->ifi_obytes;
Jim109
źródło
4

Szybka wersja zaakceptowanej odpowiedzi. Dzielę również kod na mniejsze jednostki.

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
    }
}
Adnan Aftab
źródło
4

Poprawiłem powyższy kod źródłowy do wersji 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
    }
}
Jonghee Park
źródło
4

Nowa wersja oparta na poprzednich wersjach, ale dostosowana do Swift4 i 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
    }
}
dede.exe
źródło
czy wyświetla się dla każdej aplikacji czy rzeczywistej aplikacji, w której jest używana?
Maksim Kniazev
To globalne użycie ... Nie jest oddzielone aplikacją.
dede.exe
Wartość pochodzi z ostatniego restartu. Nie możesz dostać się za miniony / bieżący miesiąc?
Slavcho
@Slavcho. Do tej daty nie było to możliwe. Po iOS12 nadal nie przeprowadziłem nowych badań na ten temat. Zalecam gromadzenie ir w jakimś miejscu, aby później je odzyskać.
dede.exe
0

Przepraszam za tę samą odpowiedź ponownie.

ale stwierdziłem, że UInt32 nie wystarczy, więc ulega awarii, gdy staje się zbyt duży.

Właśnie zmieniłem UInt32 na UInt64 i działa dobrze.

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
    }
}
Soohwan Park
źródło