59 votes

Obtenir l'adresse IP de l'ordinateur local

En C++, quel est le moyen le plus simple d'obtenir l'adresse IP et le masque de sous-réseau de l'ordinateur local ?

Je souhaite être en mesure de détecter l'adresse IP de la machine locale dans mon réseau local. Dans mon cas particulier, j'ai un réseau avec un masque de sous-réseau de 255.255.255.0 et l'adresse IP de mon ordinateur est 192.168.0.5. Je dois obtenir ces deux valeurs de manière programmatique afin d'envoyer un message de diffusion à mon réseau (sous la forme 192.168.0.255, pour mon cas particulier).

Edit : De nombreuses réponses ne donnaient pas les résultats que j'attendais car j'avais deux IP de réseau différentes. Torial a fait l'affaire (il m'a donné les deux adresses IP). Merci.

Edit 2 : Merci à Brian R. Bondy pour l'information sur le masque de sous-réseau.

0 votes

Re : 169.254.47.253, il semble que vous n'ayez pas de routeur et que ce soit votre adresse externe.

39voto

Jeremy Friesner Points 16684

La question est plus délicate qu'il n'y paraît, car dans de nombreux cas, il n'y a pas tant "une adresse IP pour l'ordinateur local" qu'un certain nombre d'adresses IP différentes. Par exemple, le Mac sur lequel je tape en ce moment (qui est une configuration Mac standard assez basique) a les adresses IP suivantes associées à lui :

fe80::1%lo0  
127.0.0.1 
::1 
fe80::21f:5bff:fe3f:1b36%en1 
10.0.0.138 
172.16.175.1
192.168.27.1

... et il ne s'agit pas non plus de déterminer laquelle de ces adresses est "la vraie adresse IP"... elles sont toutes "vraies" et utiles, certaines plus utiles que d'autres selon l'usage que vous allez en faire.

D'après mon expérience, la meilleure façon d'obtenir "une adresse IP" pour votre ordinateur local n'est pas du tout d'interroger l'ordinateur local, mais plutôt de demander à l'ordinateur avec lequel votre programme communique comment il voit l'adresse IP de votre ordinateur. Par exemple, si vous écrivez un programme client, envoyez un message au serveur en lui demandant de renvoyer comme données l'adresse IP d'où provient votre requête. De cette façon, vous saurez ce que le pertinent L'adresse IP est, compte tenu du contexte, l'ordinateur avec lequel vous communiquez.

Cela dit, cette astuce peut ne pas convenir à certains usages (par exemple, lorsque vous ne communiquez pas avec un ordinateur particulier). Parfois, il suffit donc de rassembler la liste de toutes les adresses IP associées à votre machine. La meilleure façon de le faire sous Unix/Mac (AFAIK) est d'appeler getifaddrs() et d'itérer sur les résultats. Sous Windows, essayez GetAdaptersAddresses() pour obtenir une fonctionnalité similaire. Pour des exemples d'utilisation de ces deux fonctions, voir la fonction GetNetworkInterfaceInfos() de l'application ce fichier .

1 votes

La logique de l'utilisation de requêtes en réseau au lieu de requêtes en système local semble saine. Quelle serait une façon multiplateforme de commencer à faire cela de manière pragmatique ? J'utilise C++, principalement avec Qt, mais je peux utiliser la STL (C++14) si nécessaire.

0 votes

Le serveur peut appeler getpeername() sur son socket pour obtenir l'adresse IP du client, puis renvoyer ces données au client (via la connexion TCP).

0 votes

@Jeremy. A mon avis, interroger un serveur pour obtenir une IP locale n'est pas une solution élégante. Je suggère d'énumérer les IP locales pour l'IP attachée à la passerelle. *Il est vrai que très occasionnellement (par exemple dans les réseaux d'entreprise), il peut y avoir plus d'une passerelle.

23voto

kgriffs Points 1839

Le problème avec toutes les approches basées sur gethostbyname est que vous n'obtiendrez pas toutes les adresses IP attribuées à une machine particulière. Les serveurs ont généralement plus d'un adaptateur.

Voici un exemple de la façon dont vous pouvez itérer à travers toutes les adresses Ipv4 et Ipv6 sur la machine hôte :

void ListIpAddresses(IpAddresses& ipAddrs)
{
  IP_ADAPTER_ADDRESSES* adapter_addresses(NULL);
  IP_ADAPTER_ADDRESSES* adapter(NULL);

  // Start with a 16 KB buffer and resize if needed -
  // multiple attempts in case interfaces change while
  // we are in the middle of querying them.
  DWORD adapter_addresses_buffer_size = 16 * KB;
  for (int attempts = 0; attempts != 3; ++attempts)
  {
    adapter_addresses = (IP_ADAPTER_ADDRESSES*)malloc(adapter_addresses_buffer_size);
    assert(adapter_addresses);

    DWORD error = ::GetAdaptersAddresses(
      AF_UNSPEC, 
      GAA_FLAG_SKIP_ANYCAST | 
        GAA_FLAG_SKIP_MULTICAST | 
        GAA_FLAG_SKIP_DNS_SERVER |
        GAA_FLAG_SKIP_FRIENDLY_NAME, 
      NULL, 
      adapter_addresses,
      &adapter_addresses_buffer_size);

    if (ERROR_SUCCESS == error)
    {
      // We're done here, people!
      break;
    }
    else if (ERROR_BUFFER_OVERFLOW == error)
    {
      // Try again with the new size
      free(adapter_addresses);
      adapter_addresses = NULL;

      continue;
    }
    else
    {
      // Unexpected error code - log and throw
      free(adapter_addresses);
      adapter_addresses = NULL;

      // @todo
      LOG_AND_THROW_HERE();
    }
  }

  // Iterate through all of the adapters
  for (adapter = adapter_addresses; NULL != adapter; adapter = adapter->Next)
  {
    // Skip loopback adapters
    if (IF_TYPE_SOFTWARE_LOOPBACK == adapter->IfType)
    {
      continue;
    }

    // Parse all IPv4 and IPv6 addresses
    for (
      IP_ADAPTER_UNICAST_ADDRESS* address = adapter->FirstUnicastAddress; 
      NULL != address;
      address = address->Next)
    {
      auto family = address->Address.lpSockaddr->sa_family;
      if (AF_INET == family)
      {
        // IPv4
        SOCKADDR_IN* ipv4 = reinterpret_cast<SOCKADDR_IN*>(address->Address.lpSockaddr);

        char str_buffer[INET_ADDRSTRLEN] = {0};
        inet_ntop(AF_INET, &(ipv4->sin_addr), str_buffer, INET_ADDRSTRLEN);
        ipAddrs.mIpv4.push_back(str_buffer);
      }
      else if (AF_INET6 == family)
      {
        // IPv6
        SOCKADDR_IN6* ipv6 = reinterpret_cast<SOCKADDR_IN6*>(address->Address.lpSockaddr);

        char str_buffer[INET6_ADDRSTRLEN] = {0};
        inet_ntop(AF_INET6, &(ipv6->sin6_addr), str_buffer, INET6_ADDRSTRLEN);

        std::string ipv6_str(str_buffer);

        // Detect and skip non-external addresses
        bool is_link_local(false);
        bool is_special_use(false);

        if (0 == ipv6_str.find("fe"))
        {
          char c = ipv6_str[2];
          if (c == '8' || c == '9' || c == 'a' || c == 'b')
          {
            is_link_local = true;
          }
        }
        else if (0 == ipv6_str.find("2001:0:"))
        {
          is_special_use = true;
        }

        if (! (is_link_local || is_special_use))
        {
          ipAddrs.mIpv6.push_back(ipv6_str);
        }
      }
      else
      {
        // Skip all other types of addresses
        continue;
      }
    }
  }

  // Cleanup
  free(adapter_addresses);
  adapter_addresses = NULL;

  // Cheers!
}

0 votes

Cela fonctionne très bien sous Windows. Quelqu'un sait-il s'il existe un moyen équivalent d'y parvenir sous Linux (ou, plus généralement, pour les systèmes d'exploitation compatibles POSIX) ?

1 votes

Merci pour ce travail, mais quels #includes sont nécessaires pour que le code fonctionne ? Je ne les trouve pas tous

21voto

Brian R. Bondy Points 141769

Vous pouvez utiliser gethostname suivi de gethostbyname pour obtenir l'IP interne de votre interface locale.

Cette adresse IP renvoyée peut cependant être différente de votre adresse IP externe. Pour obtenir votre IP externe, vous devez communiquer avec un serveur externe qui vous dira quelle est votre IP externe. En effet, l'IP externe n'est pas la vôtre, mais celle de votre routeur.

//Example: b1 == 192, b2 == 168, b3 == 0, b4 == 100
struct IPv4
{
    unsigned char b1, b2, b3, b4;
};

bool getMyIP(IPv4 & myIP)
{
    char szBuffer[1024];

    #ifdef WIN32
    WSADATA wsaData;
    WORD wVersionRequested = MAKEWORD(2, 0);
    if(::WSAStartup(wVersionRequested, &wsaData) != 0)
        return false;
    #endif

    if(gethostname(szBuffer, sizeof(szBuffer)) == SOCKET_ERROR)
    {
      #ifdef WIN32
      WSACleanup();
      #endif
      return false;
    }

    struct hostent *host = gethostbyname(szBuffer);
    if(host == NULL)
    {
      #ifdef WIN32
      WSACleanup();
      #endif
      return false;
    }

    //Obtain the computer's IP
    myIP.b1 = ((struct in_addr *)(host->h_addr))->S_un.S_un_b.s_b1;
    myIP.b2 = ((struct in_addr *)(host->h_addr))->S_un.S_un_b.s_b2;
    myIP.b3 = ((struct in_addr *)(host->h_addr))->S_un.S_un_b.s_b3;
    myIP.b4 = ((struct in_addr *)(host->h_addr))->S_un.S_un_b.s_b4;

    #ifdef WIN32
    WSACleanup();
    #endif
    return true;
}

Vous pouvez aussi toujours utiliser simplement 127.0.0.1 qui représente toujours la machine locale.

Masque de sous-réseau dans Windows :

Vous pouvez obtenir le masque de sous-réseau (ainsi que la passerelle et d'autres informations) en interrogeant les sous-clés de cette entrée de registre :

HKEY_LOCAL_MACHINE \SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces

Recherchez la valeur de registre SubnetMask.

Autres méthodes pour obtenir des informations sur l'interface dans Windows :

Vous pouvez également retrouver les informations que vous recherchez en utilisant : WSAIoctl avec cette option : SIO_GET_INTERFACE_LIST

0 votes

2 votes

gethostbyname est maintenant déprécié. Utilisez getaddrinfo() o GetAddrInfoW() au lieu de

11voto

torial Points 9883

Si vous utilisez winsock, voici un moyen : http://tangentsoft.net/wskfaq/examples/ipaddr.html

1voto

PhiLho Points 23458

Comment obtenir l'adresse IP de la machine locale sur le réseau ? semble décrire la solution assez bien...

2 votes

Le lien est mort... mais ce n'est pas une surprise, car cela fait cinq ans que le message original a été publié !

4 votes

Bien vu, ça a marché ! Merci. Voir ici... web.archive.org/web/20100708151539/http://…

0 votes

@patrickvacek Malheureusement le nouveau lien utilise aussi gethostbyname() qui est déprécié. Il faut utiliser getaddrinfo() ou GetAddrInfoW() mais qui peut retourner plusieurs adresses.

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