Connaitre son adresse IP en cocoa

ettiboettibo Membre
novembre 2011 modifié dans Vos applications #1
Bonjour à  tous,
je voulais savoir s'il y avait un moyen de connaitre facilement son adresse IP en cocoa?
Je voudrais faire un système où mon ipad soit connecté en wifi, regarde son ip au lancement de l'application, puis écoute sur l'adresse de broadcast, à  ce moment là , le serveur va envoyer son ip sur l'adresse de broadcast. C'est alors que l'ipad va récupéré cette adresse ipV4 sur 4 octets pour se connecter dessus.
Merci d'avance.

Réponses

  • AliGatorAliGator Membre, Modérateur
    06:14 modifié #2
    Je ne comprend pas trop quelle adresse IP tu veux récupérer (la locale, l'interne, celle vue de l'extérieur, ...), si tu veux gérer uniquement le cas Wifi, quid d'un iPad 3G, etc ?

    Et pourquoi ne pas utiliser Bonjour/ZeroConf, cela ne répondrait-il pas à  ta question ?
    NSNetService Programming Guide
    BonjourWeb : Un Sample Code d'Apple

    Et sinon en faisant une petite recherche rapide on trouve plein de samples. Objective-C étant du C, toutes les réponses retournées par Google permettant de faire ça en C sont donc acceptables, comme celle-ci (pas testée donc à  valider)

    Voilà  pour les pistes !
  • zoczoc Membre
    06:14 modifié #3
    Pareil que Ali... Ce que tu veux faire existe déjà , ça s'appelle Bonjour (terminologie Apple) / ZeroConf (terminologie officielle).


    Et il y a déjà  tout ce qu'il faut dans l'API iOS pour utiliser Bonjour (autant en tant que serveur qu'en tant que client).

  • ettiboettibo Membre
    06:14 modifié #4
    Ali, pour répondre à  ta question.
    Quand tu es connecté sur un réseau, ton device a une ip, et je voulais la connaitre pour ensuite accéder à  mon adresse de broadcast.
    Je vais regarder du coté de Bonjour et ZeroConf alors.
    Merci
  • ettiboettibo Membre
    06:14 modifié #5
    Ouah, ça me parait super lourd pour un truc super simple qui me prendait 5 lignes Bonjour !!!
  • AliGatorAliGator Membre, Modérateur
    06:14 modifié #6
    dans 1321361212:

    Ali, pour répondre à  ta question.
    Quand tu es connecté sur un réseau, ton device a une ip
    Justement pas tout à  fait. Quand tu es connecté sur un réseau, ton device à  plusieurs IP.
    Une ou plusieurs IP par interface réseau (l'iPad Wifi en a au moins 3 je pense, en0, bt0 et lo0 sans doute), dont dans la boucle une IP locale donc.
    Bon après je suppose que tu veux l'IP LAN sur l'interface réseau correspondant au  Wifi, et pas l'IP WAN (et encore moins la loopback), mais je posais la question car juste "l'adresse IP" ça peut ne pas suffir pour décrire quelle IP tu veux (et ça dépend de la topologie de ton réseau aussi, bien que là  encore j'imagine que tu as un réseau domestique assez classique avec une Box qui génère le réseau Wifi, et pas un VLAN ou une borne Wifi qui fait Bridge, mais comme je sais pas c'est aussi une question à  se poser)
  • ettiboettibo Membre
    novembre 2011 modifié #7
    Alors, voilà , j'ai trouvé la solution suivante si ça intéresse des gens.

    Il est très important de faire ces deux includes

    #include <ifaddrs.h>
    #include <arpa/inet.h>

    - (void)getBroadcastIp<br />{<br />&nbsp; &nbsp; NSString *address = @&quot;error&quot;;<br />&nbsp; &nbsp; struct ifaddrs *interfaces = NULL;<br />&nbsp; &nbsp; struct ifaddrs *temp_addr = NULL;<br />&nbsp; &nbsp; int success = 0;<br />&nbsp; &nbsp; <br />&nbsp; &nbsp; // retrieve the current interfaces - returns 0 on success<br />&nbsp; &nbsp; success = getifaddrs(&amp;interfaces);<br />&nbsp; &nbsp; if (success == 0)<br />&nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; // Loop through linked list of interfaces<br />&nbsp; &nbsp; &nbsp; &nbsp; temp_addr = interfaces;<br />&nbsp; &nbsp; &nbsp; &nbsp; while(temp_addr != NULL)<br />&nbsp; &nbsp; &nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(temp_addr-&gt;ifa_addr-&gt;sa_family == AF_INET)<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Check if interface is en0 which is the wifi connection on the iPhone<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if([[NSString stringWithUTF8String:temp_addr-&gt;ifa_name] isEqualToString:@&quot;en0&quot;])<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Get NSString from C String<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr-&gt;ifa_addr)-&gt;sin_addr)];<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; temp_addr = temp_addr-&gt;ifa_next;<br />&nbsp; &nbsp; &nbsp; &nbsp; }<br />&nbsp; &nbsp; }<br />&nbsp; &nbsp; // Free memory<br />&nbsp; &nbsp; freeifaddrs(interfaces);<br /><br />&nbsp; &nbsp; NSArray&nbsp; &nbsp; &nbsp; &nbsp;  *tmp = [address componentsSeparatedByString: @&quot;.&quot;];<br />&nbsp; &nbsp; NSInteger&nbsp; &nbsp; &nbsp;  max = [tmp count];<br />&nbsp; &nbsp; NSString&nbsp; &nbsp; &nbsp; &nbsp; *beginAdrr = @&quot;&quot;;<br />&nbsp; &nbsp; for (NSInteger i = 0; i &lt; max; i++)<br />&nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; if (i != (max - 1))<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; beginAdrr = [beginAdrr stringByAppendingFormat:@&quot;%@.&quot;, [tmp objectAtIndex:i]];<br />&nbsp; &nbsp; }<br />&nbsp; &nbsp; NSString&nbsp; &nbsp; *broadcastAddr = [NSString stringWithFormat:@&quot;%@%d&quot;, beginAdrr, 255];<br />&nbsp; &nbsp; NSLog(@&quot;BroadCast: %@&quot;, broadcastAddr);<br />}<br />
    
  • AliGatorAliGator Membre, Modérateur
    novembre 2011 modifié #8
    Ce qui confirme ce que je disais plus haut avec les diverses interfaces réseau à  comparer  :D ;)

    Ceci dit le début du code est bien, mais la fin c'est plutôt foireux (à  la fois pas clean mais loin d'être exact en plus) :
    • Déjà  par souci de testabilité et maintenance ça devrait être séparé en 2 méthodes je trouve
    • Mais surtout la manipulation des adresses IP pour générer l'IP de broadcast qui est faitesous forme de chaà®ne au lieu de sous forme d'un tuple d'entiers (typiquement structure sockaddr_in). Moins coûteux, plus logique dans la phylosophie POO, moins de risque à  éviter les conversions dans tous les sens
    • Mais surtout ce bout de code présuppose que l'IP de broadcast c'est juste l'IP de l'iPad mais dont on a remplacé le dernier octet par 255. Ce qui n'est pas vrai, rien ne garantit que l'IP de ton iPad soit forcément une IP de classe C
    Ce n'est clairement pas la manière de faire pour obtenir l'IP de broadcast du sous-réseau. Il faut à  la place masquer l'IP de la machine avec le masque de sous-réseau pour avoir l'adresse de sous-réseau, et mettre ensuite tous les bits restants à  1.

    Sinon pour tous les sous-réseaux qui ne seront pas de classe C (type /24) ton code en plus d'être lourd de conversions et d'une approche étrange, ne va pas marcher et ne pas te retourner la bonne valeur du tout...
  • zoczoc Membre
    06:14 modifié #9
    dans 1321362673:

    Ouah, ça me parait super lourd pour un truc super simple qui me prendait 5 lignes Bonjour !!!

    Ouais, mais au moins c'est standard puisqu'utilisé par tout un tas de périphériques réseau, comme les imprimantes.



  • ettiboettibo Membre
    06:14 modifié #10
    En fait Ali, pour le protocole que l'on a établi, notre serveur écrira toujours sur cette adresse de broadcast, je veux dire finissant par .255.
    Si tu as une solution plus propre que celle que j'ai faite, je suis tout à  fait preneur.
    Merci :D
  • AliGatorAliGator Membre, Modérateur
    06:14 modifié #11
    Y'a 100x plus propre, oui, comme j'ai décrit.

    Typiquement :
    - une méthode qui retourne l'IP de l'iPad en retour, et pas sous forme de NSString mais sous forme d'une structure du genre sockaddr_in ou équivalent, ou éventuellement un NSData les encapsulant, enfin bref les 4 octets de l'IP et pas leur représentation décimale en NSString qui n'est qu'une représentation (tu mélanges Vue et Modèle là ), et tant qu'on y est le masque de sous-réseau qui doit pas être très loin à  récupérer si on a l'IP, genre dans le ifa_addr ou pas loin
    - une méthode qui à  partir de l'IP (sous forme de 4 octets toujours, aucune raison de passer en NSString et faire des conversions à  ce stade) et du masque de sous-réseau (idem) calcule l'adresse de broadcast (facile avec des bitwise masks, genre [tt]long broadcastAddr = (long)ip | ~(long)mask[/tt] un truc dans le genre
    - Et seulement à  la toute fin, uniquement au moment où tu as besoin de représenter ton IP sous forme de chaà®ne avec les valeurs décimales des octets séparés par des points (façon "a.b.c.d" quoi), formatter cette IP avec ce format (faut pas mélanger les diverses parties du MVC !!)
  • laudemalaudema Membre
    novembre 2011 modifié #12
    Ali te propose de rester dans la partie C (non objet) tant que tu travailles sur tes adresses et d'être à  peu près certain que ton utilisateur trouvera ton service quelque soit la configuration du réseau sur lequel ton application sera activée. Et de mettre un peu d'ordre pour utiliser tout ça de manière "Objet" ensuite.
    Si tu es certain d'une adresse IP "Class C" (192.168.XXX.XXX) tu peux garder ta méthode pour trouver l'adresse de broadcast mais en général on ne sait pas quel matériel un utilisateur va utiliser et si tu creuses un peu les fonctions Posix (je crois que c'est du Posix) tu verras que c'est puissant, rapide et très efficace. Quoiqu'un peu rugueux :)

    Pour voir à  quoi ça ressemble tu ouvres la fenêtre de la documentation (dans l'Organizer pour Xcode 4) et tu cherches après la fonction que tu utilises "getifaddrs". ça donne, dans les références, la "Man page" de getifaddrs -- get interface addresses avec aussi des liens dans le texte qui permettent de creuser un peu le sujet sans aller sur Google et, dans Sample Code, un exemple que tu peux ouvrir dans Xcode pour étude via le debugger (en fait je n'ai pas regardé celui là  précisément mais souvent on peut les faire tourner et en disséquer le fonctionnement pas à  pas). Autre avantage de la démo: tu retrouves ce que dit Ali, notre maà®tre à  tous  o:) sur la manière dont en POO les données de ta classe sont "encapsulées" et les calculs en interne séparés (c'est le modèle) et la manière d'y accéder proprement dans ton Controller.
    Tout dépend de ce que tu espères faire avec ton application une fois qu'elle sera achevée et de ton envie d'apprendre des choses ;)

    PS: sans l'avoir fait tourner j'ai compilé le projet pour le débarrasser des erreurs et warnings dus au fait qu'il était destiné à  d'anciennes versions de l'OS et te le mets en pièce jointe
  • ettiboettibo Membre
    novembre 2011 modifié #13
    Bonjour, bonjour,

    suite à  vos conseils, je me suis remis à  penser en mode C, en effet, vous avez raison, ça me parait beaucoup plus propre avec cette méthode et en plus, j'ai une adresse de broadcast différente (par différente, j'entends qui s'adapte vraiment au masque de sous réseau).  :p :p :D
    Si ça intéresse des gens.
    - (NSString *)broadcastAddressForAddress:(NSString *)ipAddress withMask:(NSString *)netmask {<br />&nbsp; &nbsp; NSAssert(nil != ipAddress, @&quot;IP address cannot be nil&quot;);<br />&nbsp; &nbsp; NSAssert(nil != netmask, @&quot;Netmask cannot be nil&quot;);<br />&nbsp; &nbsp; NSArray *ipChunks = [ipAddress componentsSeparatedByString:@&quot;.&quot;];<br />&nbsp; &nbsp; NSAssert([ipChunks count] == 4, @&quot;IP does not have 4 octets!&quot;);<br />&nbsp; &nbsp; NSArray *nmChunks = [netmask componentsSeparatedByString:@&quot;.&quot;];<br />&nbsp; &nbsp; NSAssert([nmChunks count] == 4, @&quot;Netmask does not have 4 octets!&quot;);<br />&nbsp; &nbsp; <br />&nbsp; &nbsp; NSUInteger ipRaw = 0;<br />&nbsp; &nbsp; NSUInteger nmRaw = 0;<br />&nbsp; &nbsp; NSUInteger shift = 24;<br />&nbsp; &nbsp; for (NSUInteger i = 0; i &lt; 4; ++i, shift -= 8) {<br />&nbsp; &nbsp; &nbsp; &nbsp; ipRaw |= [[ipChunks objectAtIndex:i] intValue] &lt;&lt; shift;<br />&nbsp; &nbsp; &nbsp; &nbsp; nmRaw |= [[nmChunks objectAtIndex:i] intValue] &lt;&lt; shift;<br />&nbsp; &nbsp; }<br />&nbsp; &nbsp; <br />&nbsp; &nbsp; NSUInteger bcRaw = ~nmRaw | ipRaw;<br />&nbsp; &nbsp; return [NSString stringWithFormat:@&quot;%d.%d.%d.%d&quot;, (bcRaw &amp; 0xFF000000) &gt;&gt; 24,<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (bcRaw &amp; 0x00FF0000) &gt;&gt; 16, (bcRaw &amp; 0x0000FF00) &gt;&gt; 8, bcRaw &amp; 0x000000FF];<br />}<br /><br />-(NSString*)getWifiIpAddress<br />{<br />&nbsp; &nbsp; NSString *address = @&quot;error&quot;;<br />&nbsp; &nbsp; struct ifaddrs *interfaces = NULL;<br />&nbsp; &nbsp; struct ifaddrs *temp_addr = NULL;<br />&nbsp; &nbsp; int success = 0;<br />&nbsp; &nbsp; <br />&nbsp; &nbsp; // retrieve the current interfaces - returns 0 on success<br />&nbsp; &nbsp; success = getifaddrs(&amp;interfaces);<br />&nbsp; &nbsp; if (success == 0)<br />&nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; // Loop through linked list of interfaces<br />&nbsp; &nbsp; &nbsp; &nbsp; temp_addr = interfaces;<br />&nbsp; &nbsp; &nbsp; &nbsp; while(temp_addr != NULL)<br />&nbsp; &nbsp; &nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(temp_addr-&gt;ifa_addr-&gt;sa_family == AF_INET)<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Check if interface is en0 which is the wifi connection on the iPhone<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if([[NSString stringWithUTF8String:temp_addr-&gt;ifa_name] isEqualToString:@&quot;en0&quot;])<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Get NSString from C String<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //NSLog(@&quot;la %d&quot;, inet_aton(inet_ntoa( ((struct sockaddr_in *)temp_addr-&gt;ifa_addr)-&gt;sin_addr), &amp;((struct sockaddr_in *)temp_addr-&gt;ifa_addr)-&gt;sin_addr));<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr-&gt;ifa_addr)-&gt;sin_addr)];<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  // NSLog(@&quot;ici %d&quot;, ((struct sockaddr_in *)temp_addr-&gt;ifa_addr)-&gt;sin_addr.s_addr);<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; temp_addr = temp_addr-&gt;ifa_next;<br />&nbsp; &nbsp; &nbsp; &nbsp; }<br />&nbsp; &nbsp; }<br />&nbsp; &nbsp; // Free memory<br />&nbsp; &nbsp; freeifaddrs(interfaces);<br />&nbsp; &nbsp; return address;<br />}<br /><br /><br />- (NSString *)netmaskForWifiInterface{<br />&nbsp; &nbsp; NSString*&nbsp;  ifName = @&quot;en0&quot;;<br />&nbsp; &nbsp; NSAssert(nil != ifName, @&quot;Interface name cannot be nil&quot;);<br />&nbsp; &nbsp; <br />&nbsp; &nbsp; struct ifreq ifr;<br />&nbsp; &nbsp; strncpy(ifr.ifr_name, [ifName UTF8String], IFNAMSIZ-1);<br />&nbsp; &nbsp; int fd = socket(AF_INET, SOCK_DGRAM, 0);<br />&nbsp; &nbsp; if (-1 == fd) {<br />&nbsp; &nbsp; &nbsp; &nbsp; NSLog(@&quot;Failed to open socket to get netmask&quot;);<br />&nbsp; &nbsp; &nbsp; &nbsp; return nil;<br />&nbsp; &nbsp; }<br />&nbsp; &nbsp; <br />&nbsp; &nbsp; if (-1 == ioctl(fd, SIOCGIFNETMASK, &amp;ifr)) {<br />&nbsp; &nbsp; &nbsp; &nbsp; NSLog(@&quot;Failed to read netmask: %@&quot;, [NSString stringWithFormat:@&quot;%s&quot;, strerror(errno)]);<br />&nbsp; &nbsp; &nbsp; &nbsp; close(fd);<br />&nbsp; &nbsp; &nbsp; &nbsp; return nil;<br />&nbsp; &nbsp; }<br />&nbsp; &nbsp; <br />&nbsp; &nbsp; close(fd);<br />&nbsp; &nbsp; char *cstring = inet_ntoa(((struct sockaddr_in *)&amp;ifr.ifr_addr)-&gt;sin_addr);<br />&nbsp; &nbsp; return [NSString stringWithFormat:@&quot;%s&quot;, cstring];<br />}<br /><br />- (NSString*)getBroadcastAddress<br />{<br /><br />&nbsp; &nbsp; NSString *ipAdress = [self getWifiIpAddress];<br />&nbsp; &nbsp; NSString *netMask = [self netmaskForWifiInterface];<br />&nbsp; &nbsp; NSString *broadcastAddr = [self broadcastAddressForAddress:ipAdress withMask:netMask];<br />&nbsp; &nbsp; return (broadcastAddr);<br />}<br />
    


    voilà , dites moi ce que vous en pensez
  • AliGatorAliGator Membre, Modérateur
    06:14 modifié #14
    J'ai pas tout lu mais ça me semble pas mal et déjà  beaucoup beaucoup plus clair ! :) :)

    En dernier point pour faire un truc propre je manipulerais les adresses IP avec une classe ou structure dédiée.
    Par exemple, c'est vrai que les structures genre sin_addr utilisées par les librairies C bas niveau sont parfois un peu déroutante, mais par contre tu peux toujours prévoir une classe IPAddress par exemple pour manipuler des adresses IP et les passer d'une méthode à  l'autre. Ou même plus simple si tu veux pas créer de classe, un simple [tt]typedef union { unsigned long bytes; unsigned char byte[4]; } ip_addr;[/tt] par exemple peut suffire, pour avoir un type qui permette de véhiculer 4 octets qui peuvent tout aussi bien être interprétés comme un gros entier long -- pratique pour faire le masque entre l'IP et le masque de sous réseau en une seule opération plutôt que d'avoir à  découper octet par octet et faire une boucle for -- que comme 4 octets courts séparés (pratique pour la représenter en une chaine "a.b.c.d" au dernier moment.

    Du coup tu adaptes ton code pour que [tt]getWifiIpAddress[/tt] et [tt]netmaskForWifiInterface[/tt] retournent chacun une structure ip_addr (ou la classe IPAddress si tu as préféré créer une classe) et non plus une NSString (et surtout sans passer par une conversion sin_addr->cstring->NSString->ip_addr, autant directement construire ton ip_addr à  partir de sin_addr)

    Et ensuite le code de pourra tout simplement être :
    ip_addr ip = [self getWifiIpAddress];<br />ip_addr nm = [self netmaskForWifiInterface];<br />ip_addr bc;<br />bc.bytes = ip.bytes | ~nm.bytes;<br />NSString* broadcastAddrAsString = [NSString stringWithFormat:@&quot;%d.%d.%d.%d&quot;,bc.byte[0], bc.byte[1], bc.byte[2], bc.byte[3]];
    
    Même plus besoin de tout ton code de boucles for et de redécoupage des NSString et réinterprétation en intValue dans ton [tt]broadcastAddressForAddress:withMask:[/tt] !


    Bon en pratique plutôt que de faire ça, tu peux aussi donc créer plutôt une classe IPAddress si tu préfères plutôt que ce typedef d'union que j'ai fait, ou sinon plutôt que de t'embêter le plus simple reste d'utiliser ce qui existe déjà , à  savoir la structure "sin_addr" qui représente déjà  les octets d'une adresse IP, donc pourquoi réinventer la roue...
Connectez-vous ou Inscrivez-vous pour répondre.