web-dev-qa-db-fra.com

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 veux pouvoir détecter l'adresse IP de la machine locale sur 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 par programme afin d'envoyer un message de diffusion à mon réseau (sous la forme 192.168.0.255, pour mon cas particulier)

Edit: Beaucoup de réponses ne donnaient pas les résultats escomptés parce que j'avais deux adresses IP de réseau différentes. Le code de 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.

46
djeidot

La question est plus délicate qu’elle ne le semble, car dans de nombreux cas, il n’existe pas "une adresse IP pour l’ordinateur local", mais un certain nombre d’adresses IP différentes. Par exemple, le Mac sur lequel je tape en ce moment (qui est une configuration de base Mac assez basique) est associé aux adresses IP suivantes:

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 suffit pas de savoir laquelle des réponses ci-dessus est "la véritable adresse IP", non plus ... elles sont toutes "réelles" et utiles; Certaines sont plus utiles que d’autres, en fonction de l’utilisation des adresses.

D'après mon expérience, le meilleur moyen 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 considère l'adresse IP de votre ordinateur. par exemple. Si vous écrivez un programme client, envoyez un message au serveur pour lui demander de renvoyer sous forme de données l'adresse IP d'où provient votre demande. Ainsi, vous saurez quelle est l'adresse IP pertinente, en fonction du contexte de l'ordinateur avec lequel vous communiquez.

Cela dit, cette astuce peut ne pas convenir à certaines fins (par exemple, lorsque vous ne communiquez pas avec un ordinateur particulier). Vous devez donc parfois rassembler la liste de toutes les adresses IP associées à votre ordinateur. La meilleure façon de faire cela sous Unix/Mac (AFAIK) est d'appeler getifaddrs () et de faire une itération sur les résultats. Sous Windows, essayez GetAdaptersAddresses () pour obtenir une fonctionnalité similaire. Pour des exemples d'utilisation des deux, voir la fonction GetNetworkInterfaceInfos () dans ce fichier .

31
Jeremy Friesner

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 plusieurs adaptateurs.

Voici un exemple de la façon dont vous pouvez parcourir 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!
}
20
kgriffs

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

Cette adresse IP renvoyée peut toutefois être différente de votre adresse IP externe. Pour obtenir votre adresse IP externe, vous devez communiquer avec un serveur externe qui vous indiquera en quoi consiste votre adresse IP externe. Parce que l'IP externe n'est pas à vous mais à vos routeurs.

//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 également simplement utiliser 127.0.0.1 qui représente toujours la machine locale.

Masque de sous-réseau sous Windows:

Vous pouvez obtenir le masque de sous-réseau (et 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 sous Windows:

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

15
Brian R. Bondy

Vous ne pouvez pas faire cela en C++ standard.

Je poste ceci parce que c'est la seule réponse correcte. Votre question demande comment faire en C++. Eh bien, vous ne pouvez pas le faire en C++. Vous pouvez le faire sous Windows, POSIX, Linux, Android, mais toutes ces solutions sont spécifiques à OS et ne font pas partie du standard de langue.

Le standard C++ n'a pas de couche réseau.

Je suppose que cette hypothèse erronée est que C++ Standard définit la même étendue de fonctionnalités que d’autres normes de langage, Java. Bien que Java puisse avoir un réseau intégré (et même une structure d'interface graphique) dans la propre bibliothèque standard du langage, C++ n'en a pas.

Bien qu'il existe des API et des bibliothèques tierces pouvant être utilisées par un programme C++, cela ne revient en aucun cas à dire que vous pouvez le faire en C++.

Voici un exemple pour clarifier ce que je veux dire. Vous pouvez ouvrir un fichier en C++ car il contient une classe fstream dans sa bibliothèque standard. Ce n'est pas la même chose que d'utiliser CreateFile(), une fonction spécifique à Windows et disponible uniquement pour WINAPI.

6
sashoalm

Notez également que "l'adresse IP locale" peut ne pas être une chose particulièrement unique. Si vous utilisez plusieurs réseaux physiques (câblé + sans fil + Bluetooth, par exemple) ou si vous avez un serveur avec de nombreuses cartes Ethernet, etc.) ou si vous avez configuré les interfaces TAP/TUN, votre ordinateur peut facilement disposer de nombreuses interfaces. 

4
jakobengblom2

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

3
PhiLho

Winsock spécifique:

// Init WinSock
WSADATA wsa_Data;
int wsa_ReturnCode = WSAStartup(0x101,&wsa_Data);

// Get the local hostname
char szHostName[255];
gethostname(szHostName, 255);
struct hostent *Host_entry;
Host_entry=gethostbyname(szHostName);
char * szLocalIP;
szLocalIP = inet_ntoa (*(struct in_addr *)*Host_entry->h_addr_list);
WSACleanup();
2
GEOCHET

de torial: Si vous utilisez winsock, voici une solution: http://tangentsoft.net/wskfaq/examples/ipaddr.html

En ce qui concerne la partie sous-réseau de la question; il n'existe pas de moyen indépendant de la plate-forme pour extraire le masque de sous-réseau, car l'API de socket POSIX (que tous les systèmes d'exploitation modernes implémentent) ne le spécifie pas. Vous devrez donc utiliser la méthode disponible sur la plate-forme que vous utilisez.

2
nymacro

J'ai pu le faire en utilisant le service DNS sous VS2013 avec le code suivant:

#include <Windns.h>

WSADATA wsa_Data;

int wsa_ReturnCode = WSAStartup(0x101, &wsa_Data);

gethostname(hostName, 256);
PDNS_RECORD pDnsRecord;

DNS_STATUS statsus = DnsQuery(hostName, DNS_TYPE_A, DNS_QUERY_STANDARD, NULL, &pDnsRecord, NULL);
IN_ADDR ipaddr;
ipaddr.S_un.S_addr = (pDnsRecord->Data.A.IpAddress);
printf("The IP address of the Host %s is %s \n", hostName, inet_ntoa(ipaddr));

DnsRecordListFree(&pDnsRecord, DnsFreeRecordList);

J'ai dû ajouter Dnsapi.lib en tant que dépendance addictionnelle dans l'option de l'éditeur de liens.

Référence ici .

1
Zac

Je suggère mon code.

DllExport void get_local_ips(boost::container::vector<wstring>& ips)
{
   IP_ADAPTER_ADDRESSES*       adapters  = NULL;
   IP_ADAPTER_ADDRESSES*       adapter       = NULL;
   IP_ADAPTER_UNICAST_ADDRESS* adr           = NULL;
   ULONG                       adapter_size = 0;
   ULONG                       err           = 0;
   SOCKADDR_IN*                sockaddr  = NULL;

   err = ::GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME, NULL, NULL, &adapter_size);
   adapters = (IP_ADAPTER_ADDRESSES*)malloc(adapter_size);
   err = ::GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME, NULL, adapters, &adapter_size);

   for (adapter = adapters; NULL != adapter; adapter = adapter->Next)
   {
       if (adapter->IfType     == IF_TYPE_SOFTWARE_LOOPBACK) continue; // Skip Loopback
       if (adapter->OperStatus != IfOperStatusUp) continue;            // Live connection only  

       for (adr = adapter->FirstUnicastAddress;adr != NULL; adr = adr->Next)
       {
           sockaddr = (SOCKADDR_IN*)(adr->Address.lpSockaddr);
           char    ipstr [INET6_ADDRSTRLEN] = { 0 };
           wchar_t ipwstr[INET6_ADDRSTRLEN] = { 0 };
           inet_ntop(AF_INET, &(sockaddr->sin_addr), ipstr, INET_ADDRSTRLEN);
           mbstowcs(ipwstr, ipstr, INET6_ADDRSTRLEN);
           wstring wstr(ipwstr);
           if (wstr != "0.0.0.0") ips.Push_back(wstr);                      
       }
   }

   free(adapters);
   adapters = NULL; }
0
Mark Yang

Dans DEV C++, j'ai utilisé le C pur avec WIN32, avec ce morceau de code donné:

case IDC_IP:

             gethostname(szHostName, 255);
             Host_entry=gethostbyname(szHostName);
             szLocalIP = inet_ntoa (*(struct in_addr *)*Host_entry->h_addr_list);
             //WSACleanup(); 
             writeInTextBox("\n");
             writeInTextBox("IP: "); 
             writeInTextBox(szLocalIP);
             break;

Lorsque je clique sur le bouton "show ip", cela fonctionne. Mais la deuxième fois, le programme se ferme (sans avertissement ni erreur). Quand je fais:

//WSACleanup(); 

Le programme ne se ferme pas, même en cliquant plusieurs fois sur le même bouton avec la vitesse la plus rapide . Donc, WSACleanup () risque de ne pas bien fonctionner avec Dev-C++ ..

0
Ivan

Ne pouvez-vous pas simplement envoyer à INADDR_BROADCAST ? Certes, cela va envoyer sur toutes les interfaces - mais c'est rarement un problème.

Sinon, ioctl et SIOCGIFBRDADDR devraient vous obtenir l'adresse sur * nix et WSAioctl et SIO_GET_BROADCAST_ADDRESS sur win32.

0
Mark Brackett