101 votes

Obtenir l'adresse IP de la machine

Cette question est presque identique à celle posée précédemment. Obtenir l'adresse IP de l'ordinateur local -Question. Cependant, j'ai besoin de trouver l'adresse ou les adresses IP d'un site Web. Machine Linux .

Donc : Comment puis-je - de manière programmatique dans C++ - détecter les adresses IP du serveur linux sur lequel mon application est exécutée. Les serveurs auront au moins deux adresses IP et j'ai besoin d'une adresse spécifique (celle d'un réseau donné (le réseau public)).

Je suis sûr qu'il existe une fonction simple pour le faire - mais où ?


Pour rendre les choses un peu plus claires :

  • Le serveur aura évidemment l'adresse "localhost" : 127.0.0.1
  • Le serveur aura une adresse IP interne (de gestion) : 172.16.x.x
  • Le serveur aura une adresse IP externe (publique) : 80.190.x.x

Je dois trouver l'adresse IP externe pour y lier mon application. Il est évident que je peux aussi me lier à INADDR_ANY (et c'est d'ailleurs ce que je fais pour l'instant). Mais je préférerais détecter l'adresse publique.

3 votes

Pourquoi marquer le popen d'ifconfig ? C'est vraiment la bonne chose à faire. Les bibliothèques changent, mais ifconfig et popen seront toujours là.

3 votes

Non, ifconfig , route etc. sont dépréciés pour le ip commande. Maintenant, vous devriez l'utiliser à la place.

0 votes

Ifconfig fonctionne encore, aujourd'hui, sur plus d'architectures que toute autre approche. Changez-la donc pour la commande ip quand/si c'est approprié. popen est toujours la meilleure solution.

116voto

Twelve47 Points 2682

J'ai trouvé la solution ioctl problématique sur os x (qui est conforme à POSIX donc devrait être similaire à linux). Cependant getifaddress() vous permettra de faire la même chose facilement, il fonctionne bien pour moi sur os x 10.5 et devrait être le même sous linux.

J'ai fait un exemple rapide ci-dessous qui imprimera toutes les adresses IPv4 de la machine, (vous devriez également vérifier que getifaddrs a réussi, c'est-à-dire qu'il renvoie 0).

Je l'ai mis à jour pour montrer les adresses IPv6 aussi.

#include <stdio.h>      
#include <sys/types.h>
#include <ifaddrs.h>
#include <netinet/in.h> 
#include <string.h> 
#include <arpa/inet.h>

int main (int argc, const char * argv[]) {
    struct ifaddrs * ifAddrStruct=NULL;
    struct ifaddrs * ifa=NULL;
    void * tmpAddrPtr=NULL;

    getifaddrs(&ifAddrStruct);

    for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
        if (ifa ->ifa_addr->sa_family==AF_INET) { // check it is IP4
            // is a valid IP4 Address
            tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
            char addressBuffer[INET_ADDRSTRLEN];
            inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
            printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); 
        } else if (ifa->ifa_addr->sa_family==AF_INET6) { // check it is IP6
            // is a valid IP6 Address
            tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
            char addressBuffer[INET6_ADDRSTRLEN];
            inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
            printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); 
        } 
    }
    if (ifAddrStruct!=NULL) freeifaddrs(ifAddrStruct);
    return 0;
}

7 votes

+1 et merci d'avoir apporté cette belle approche au lieu de ces ennuyeux ioctl's ! Cependant, votre gestion de l'IP6 est toujours incorrecte - vous devriez faire un cast vers sockaddr_in6, c'est-à-dire quelque chose comme tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;

2 votes

@Andrey, merci. J'ai mis à jour ma réponse (je n'ai pas eu l'occasion de la tester).

2 votes

Donne lo y wlan0 interfaces dans mon cas. Ce dont j'ai besoin est wlan0 o eth0 qui a une connexion internet. Donc, devrais-je utiliser strcmp ou il y a un meilleur moyen ?

28voto

Steve Baker Points 2220
  1. Créer un socket.
  2. Exécuter ioctl(<socketfd>, SIOCGIFCONF, (struct ifconf)&buffer);

Lisez /usr/include/linux/if.h pour des informations sur les structures ifconf et ifreq. Cela devrait vous donner l'adresse IP de chaque interface sur le système. Lisez également /usr/include/linux/sockios.h pour des ioctls supplémentaires.

0 votes

J'avais l'impression qu'il ne vous donnerait que l'adresse de l'interface à laquelle il se lie.

0 votes

@MattJoiner Non, jetez un oeil à la page de manuel, SIOCGIFCONF renvoie des informations sur toutes les interfaces. Une partie de la page de manuel citée ici : "Renvoie une liste d'adresses d'interfaces (couche transport). Cela signifie actuellement uniquement les adresses de la famille AF_INET (IPv4) pour des raisons de compatibilité. L'utilisateur passe une structure ifconf comme argument à l'ioctl. Elle contient un pointeur vers un tableau de structures ifreq dans ifc_req et sa longueur en octets dans ifc_len. Le noyau remplit les ifreqs avec toutes les adresses d'interfaces L3 en cours d'exécution "

26voto

4dan Points 189

J'aime La réponse de jjvainio . Comme le dit Zan Lnyx, il utilise la table de routage locale pour trouver l'adresse IP de l'interface Ethernet qui serait utilisée pour une connexion à un hôte externe spécifique. En utilisant un socket UDP connecté, vous pouvez obtenir l'information sans envoyer de paquets. Cette approche nécessite que vous choisissiez un hôte externe spécifique. La plupart du temps, toute adresse IP publique bien connue devrait faire l'affaire. J'aime bien l'adresse 8.8.8.8 du serveur DNS public de Google à cette fin, mais il peut arriver que vous souhaitiez choisir une autre adresse IP d'hôte externe. Voici un code qui illustre l'approche complète.

void GetPrimaryIp(char* buffer, size_t buflen) 
{
    assert(buflen >= 16);

    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    assert(sock != -1);

    const char* kGoogleDnsIp = "8.8.8.8";
    uint16_t kDnsPort = 53;
    struct sockaddr_in serv;
    memset(&serv, 0, sizeof(serv));
    serv.sin_family = AF_INET;
    serv.sin_addr.s_addr = inet_addr(kGoogleDnsIp);
    serv.sin_port = htons(kDnsPort);

    int err = connect(sock, (const sockaddr*) &serv, sizeof(serv));
    assert(err != -1);

    sockaddr_in name;
    socklen_t namelen = sizeof(name);
    err = getsockname(sock, (sockaddr*) &name, &namelen);
    assert(err != -1);

    const char* p = inet_ntop(AF_INET, &name.sin_addr, buffer, buflen);
    assert(p);

    close(sock);
}

2 votes

Cela me donne l'ip privée 127.*.*.* . peut-être parce que je suis derrière NAT ?

15voto

jjvainio Points 203

Comme vous l'avez constaté, il n'existe pas d'"adresse IP locale" unique. Voici comment trouver l'adresse locale qui peut être envoyée à un hôte spécifique.

  1. Créer un socket UDP
  2. Connectez le socket à une adresse extérieure (l'hôte qui recevra éventuellement l'adresse locale).
  3. Utilisez getsockname pour obtenir l'adresse locale

0 votes

Je ne l'ai jamais fait de cette façon mais j'aime bien. Elle utilise automatiquement la table de routage du système d'exploitation et est beaucoup plus facile que de parcourir la liste des interfaces.

5 votes

Cela suppose que vous savez ce que sera une adresse extérieure. Ce n'est souvent pas le cas dans les centres de données.

0 votes

Vous pouvez toujours en essayer trois qui sont assignés à des continents différents (l'IANA rend cela facile) et accepter les deux meilleurs sur trois.

9voto

unwind Points 181987

Cette page montre une solution utilisant les deux fonctions gethostname() en même temps que gethostbyname() . Le premier vous indique le nom de la machine locale, le second recherche la ou les adresses IP correspondant à ce nom. L'utilisation de cette dernière avec "localhost" ne fonctionne pas, car elle ne donne généralement que 127.0.0.1.

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