web-dev-qa-db-de.com

Rufen Sie die IP-Adresse des lokalen Computers ab

Wie kann man in C++ die IP-Adresse und die Subnetzmaske des lokalen Computers am einfachsten abrufen?

Ich möchte in der Lage sein, die IP-Adresse des lokalen Computers in meinem lokalen Netzwerk zu ermitteln. In meinem speziellen Fall habe ich ein Netzwerk mit einer Subnetzmaske von 255.255.255.0 und die IP-Adresse meines Computers lautet 192.168.0.5. Ich muss diese programmatisch zwei Werte erhalten, um eine Broadcast-Nachricht an mein Netzwerk zu senden (in der Form 192.168.0.255 für meinen speziellen Fall).

Edit: Viele Antworten lieferten nicht die erwarteten Ergebnisse, da ich zwei verschiedene Netzwerk-IPs hatte. Torial 's Code hat den Trick gemacht (er hat mir beide IP-Adressen gegeben). Vielen Dank. 

Edit 2: Danke an Brian R. Bondy für die Informationen zur Subnetzmaske.

46
djeidot

Die Frage ist schwieriger als es den Anschein hat, denn in vielen Fällen gibt es nicht "eine IP-Adresse für den lokalen Computer", sondern mehrere verschiedene IP-Adressen. Zum Beispiel hat der Mac, auf dem ich gerade schreibe (was ein ziemlich einfaches Standard-Mac-Setup ist) die folgenden IP-Adressen zugeordnet:

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

... und es geht nicht nur darum, herauszufinden, welche der oben genannten Adressen "die echte IP-Adresse" ist, entweder ... sie sind alle "echt" und nützlich; Einige sind nützlicher als andere, abhängig davon, für welche Adressen Sie die Adressen verwenden werden.

Nach meiner Erfahrung ist der beste Weg, eine "IP-Adresse" für Ihren lokalen Computer zu erhalten, meistens nicht, den lokalen Computer abzufragen, sondern den Computer zu fragen, mit dem Ihr Programm die IP-Adresse Ihres Computers bezeichnet. z.B. Wenn Sie ein Client-Programm schreiben, senden Sie eine Nachricht an den Server, in der der Server aufgefordert wird, die IP-Adresse, von der Ihre Anfrage stammt, als Daten zu senden. Auf diese Weise wissen Sie, was die relevante IP-Adresse ist, abhängig vom Kontext des Computers, mit dem Sie kommunizieren.

Allerdings ist dieser Trick möglicherweise nicht für bestimmte Zwecke geeignet (z. B. wenn Sie nicht mit einem bestimmten Computer kommunizieren). Daher müssen Sie manchmal nur die Liste aller IP-Adressen sammeln, die Ihrem Computer zugeordnet sind. Unter Unix/Mac (AFAIK) können Sie das am besten tun, indem Sie getifaddrs () aufrufen und die Ergebnisse durchlaufen. Versuchen Sie unter Windows GetAdaptersAddresses (), um ähnliche Funktionen zu erhalten. Beispielhafte Verwendungen von beiden finden Sie in der GetNetworkInterfaceInfos () - Funktion in dieser Datei .

31
Jeremy Friesner

Das Problem bei allen auf gethostbyname basierenden Ansätzen ist, dass Sie nicht alle IP-Adressen erhalten, die einem bestimmten Computer zugewiesen sind. Server haben normalerweise mehr als einen Adapter.

Hier ein Beispiel, wie Sie alle IPv4- und IPv6-Adressen auf dem Host-Computer durchlaufen können:

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

Sie können gethostname gefolgt von gethostbyname verwenden, um die interne IP Ihrer lokalen Schnittstelle zu erhalten. 

Diese zurückgegebene IP kann sich jedoch von Ihrer externen IP unterscheiden. Um Ihre externe IP-Adresse zu erhalten, müssen Sie mit einem externen Server kommunizieren, der Ihnen Ihre externe IP-Adresse mitteilt. Weil die externe IP-Adresse nicht Ihre ist, sondern Ihre Router.

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

Sie können auch immer 127.0.0.1 verwenden, was immer den lokalen Computer darstellt.

Subnetzmaske in Windows:

Sie können die Subnetzmaske (und das Gateway und andere Informationen) abrufen, indem Sie die Unterschlüssel dieses Registrierungseintrags abfragen:

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

Suchen Sie nach dem Registrierungswert SubnetMask.

Andere Methoden zum Abrufen von Schnittstelleninformationen in Windows:

Sie können die gewünschten Informationen auch abrufen, indem Sie Folgendes verwenden: WSAIoctl mit dieser Option: SIO_GET_INTERFACE_LIST 

15
Brian R. Bondy

In Standard C++ ist das nicht möglich.

Ich poste das, weil es die einzig richtige Antwort ist. Ihre Frage fragt, wie es in C++ gemacht wird. In C++ ist das nicht möglich. Sie können dies in Windows, POSIX, Linux, Android tun, aber alle diese Betriebssysteme sind OS-spezifische Lösungen und nicht Teil des Sprachstandards.

Standard C++ hat keine Netzwerkschicht überhaupt.

Ich gehe davon aus, dass Sie diese falsche Annahme haben, dass C++ Standard den gleichen Funktionsumfang wie andere Sprachstandards, Java, definiert. Java verfügt zwar möglicherweise über ein integriertes Netzwerk (und sogar ein GUI-Framework) in der Standardbibliothek der Sprache, C++ jedoch nicht.

Zwar gibt es Drittanbieter-APIs und -Bibliotheken, die von einem C++ - Programm verwendet werden können. Dies bedeutet jedoch nicht, dass Sie dies in C++ tun können.

Hier ist ein Beispiel, um zu verdeutlichen, was ich meine. Sie können eine Datei in C++ öffnen, da sie eine fstream-Klasse als Teil ihrer Standardbibliothek enthält. Dies ist nicht dasselbe wie bei der Verwendung von CreateFile(), einer Windows-spezifischen Funktion, die nur für WINAPI verfügbar ist.

6
sashoalm

Beachten Sie auch, dass "die lokale IP-Adresse" möglicherweise nicht besonders einzigartig ist. Wenn Sie sich in mehreren physischen Netzwerken befinden (verkabelt + drahtlos + bluetooth oder ein Server mit vielen Ethernet-Karten usw.) oder TAP/TUN-Schnittstellen einrichten, kann Ihr Computer problemlos über eine ganze Reihe von Schnittstellen verfügen. 

4
jakobengblom2

So erhalten Sie die IP-Adresse des lokalen Computers im Netzwerk scheint die Lösung recht gut zu beschreiben ...

3
PhiLho

Winsock spezifisch:

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

von torial: Wenn Sie Winsock verwenden, können Sie Folgendes tun: http://tangentsoft.net/wskfaq/examples/ipaddr.html

Wie für den Teil des Subnetzes der Frage; Es gibt keine plattformunabhängige Möglichkeit, die Subnetzmaske abzurufen, da die POSIX-Socket-API (die von allen modernen Betriebssystemen implementiert wird) dies nicht angibt. Sie müssen also jede Methode verwenden, die auf der von Ihnen verwendeten Plattform verfügbar ist.

2
nymacro

Ich konnte dies unter Verwendung des DNS-Dienstes unter VS2013 mit dem folgenden Code ausführen:

#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);

Ich musste Dnsapi.lib als abhängige Abhängigkeit in der Linker-Option hinzufügen.

Referenz hier .

1
Zac

Ich schlage meinen Code vor.

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

In DEV C++ habe ich mit WIN32 reines C mit diesem Code verwendet:

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;

Wenn ich auf den Button "show ip" klicke, funktioniert es. Beim zweiten Mal wird das Programm jedoch beendet (ohne Warnung oder Fehler). Wenn ich tue:

//WSACleanup(); 

Das Programm wird nicht beendet, auch wenn derselbe Knopf mehrmals mit der schnellsten Geschwindigkeit angeklickt wird .__ WSACleanup () funktioniert daher möglicherweise nicht gut mit Dev-C++.

0
Ivan

Können Sie nicht einfach an INADDR_BROADCAST senden? Zugegeben, das würde auf alle Schnittstellen weiterleiten - aber das ist selten ein Problem.

Andernfalls sollten ioctl und SIOCGIFBRDADDR die Adresse unter * nix und WSAioctl und SIO_GET_BROADCAST_ADDRESS unter win32 angeben.

0
Mark Brackett