138 votes

Suivi de l'utilisation des données de l'iPhone

J'ai fait des recherches sur ce sujet mais j'ai trouvé très peu de détails utiles. Avec ces détails, j'ai essayé de préparer un code comme suit.

Note : Veuillez comparer les détails partagés dans ce post avec d'autres posts avant de marquer ceci comme DUPLICAT, et pas seulement par le sujet.

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

Ce code collecte des informations sur l'utilisation de l'internet d'un appareil iPhone (et non de mon application seule).

Maintenant, si j'utilise l'internet par WiFi ou par 3G, j'obtiens les données (octets) seulement en ifi_obytes (envoyé) et ifi_ibytes (reçu) mais je pense que je devrais obtenir l'utilisation du WiFi en ifi_opackets y ifi_ipackets .

Je voulais également ajouter que si je suis connecté à un réseau WiFi, mais que je n'utilise pas l'internet, j'obtiens quand même une valeur ajoutée à la rubrique ifi_obytes y ifi_ibytes .

Il se peut que je me trompe dans l'implémentation ou la compréhension. J'ai besoin de quelqu'un pour m'aider.


Edit : Au lieu de AF_LINK J'ai essayé AF_INET ( sockaddr_in au lieu de sockaddr_dl ). Cela fait planter l'application.

177voto

user982705 Points 1121

Le fait est que pdp_ip0 est l'une des interfaces, toutes les pdpXXX son WWAN interfaces dédiées à différentes fonctions, messagerie vocale, interface réseau générale.

J'ai lu dans le forum Apple que : Le système d'exploitation ne conserve pas les statistiques réseau processus par processus. En tant que tel, il n'y a pas de solution exacte à ce problème. Vous pouvez cependant obtenir des statistiques réseau pour chaque interface réseau.

En général en0 est votre Wi-Fi et l'interface pdp_ip0 est votre WWAN interface.

Il n'y a pas de bon moyen d'obtenir des informations sur les données du réseau wifi/cellulaire depuis une date et une heure précises !

Statistique des données ( ifa_data->ifi_obytes y ifa_data->ifi_ibytes ) sont stockés lors du précédent redémarrage du dispositif.

Je ne sais pas pourquoi, mais ifi_opackets y ifi_ipackets sont montrés juste pour lo0 (Je pense que c'est l'interface principale).

Oui. L'appareil est alors connecté via WiFi et n'utilise pas l'internet if_iobytes Les valeurs viennent encore parce que cette méthode fournit des échanges d'octets en réseau et pas seulement 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)};
}

Amélioration du support du copier/coller !

1 votes

Merci beaucoup. Ce code a fonctionné à merveille. Merci aussi d'avoir expliqué tout ça.

20 votes

Vous devez importer ces bibliothèques : #include <arpa/inet.h> #include <net/if.h> #include <ifaddrs.h> #include <net/if_dl.h>

0 votes

Ai-je bien compris que les compteurs d'utilisation des données sont remis à zéro au redémarrage de l'appareil ?

16voto

Wiz Points 224

Il est important de comprendre que ces compteurs sont fournis depuis le dernier démarrage de l'appareil.

Ainsi, pour les utiliser efficacement, vous devez accompagner chaque échantillon du temps de fonctionnement de l'appareil (vous pouvez utiliser la fonction mach_absolute_time() - voir este pour plus d'informations)

Une fois que vous avez des échantillons de compteurs + le temps de fonctionnement, vous pouvez avoir une meilleure heuristique quant à l'utilisation des données...

0 votes

Mach_absolute_time n'est pas uptime. C'est en gros le temps pendant lequel l'unité centrale a été active. mach_absolute_time arrête généralement de compter lorsque le périphérique dort.

14voto

Jim109 Points 471

Pour ajouter à la réponse acceptée, il est important de réaliser que la quantité de données affichées par l'interface déborde et recommence à 0 après chaque 4 GB Il n'est pas nécessaire d'utiliser ce code, surtout si vous l'utilisez pour calculer la différence entre deux lectures. Cela est dû au fait que ifi_obytes y ifi_ibytes son uint_32 et leur valeur maximale est 4294967295 .

Aussi, je recommande d'utiliser unsigned int pour les variables contenant les données envoyées et reçues. Régulier int ont la moitié de la valeur maximale d'un entier non signé, donc lorsqu'on ajoute des ifi_obytes cela peut provoquer un débordement.

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

5voto

C_X Points 3224

Version rapide de la réponse acceptée. Je décompose également le code en petites unités.

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
    }
}

4voto

Jonghee Park Points 161

J'ai fixé le code source ci-dessus à la version 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
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X