it-swarm-eu.dev

Získejte IP adresu místního počítače

V C++ je nejjednodušší způsob, jak získat IP adresu místního počítače a masku podsítě?

Chci být schopen zjistit IP adresu lokálního počítače v lokální síti. V mém konkrétním případě mám síť s maskou podsítě 255.255.255.0 a IP adresa počítače je 192.168.0.5. Musím se dostat tyto dvě hodnoty programově, abych poslal do mé sítě vysílanou zprávu (ve formuláři 192.168.0.255, pro můj konkrétní případ)

Edit: Mnoho odpovědí neposkytlo očekávané výsledky, protože jsem měl dvě různé IP sítě. Torial 's kód udělal trik (dal mi obě IP adresy). Dík. 

Edit 2: Díky Brian R. Bondy za informaci o masce podsítě.

46
djeidot

Tato otázka je složitější, než se zdá, protože v mnoha případech neexistuje „adresa IP místního počítače“ tolik jako řada různých adres IP. Například počítače Mac, které právě píšu (což je velmi jednoduché standardní nastavení systému Mac), mají následující adresy IP:

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

... a není to jen otázka toho, která z výše uvedených je "skutečná IP adresa", buď ... jsou všechny "skutečné" a užitečné; Některé jsou užitečnější než jiné v závislosti na tom, co budete používat pro adresy.

Podle mých zkušeností často nejlepší způsob, jak získat "IP adresu" pro váš lokální počítač, není dotazovat se na lokálním počítači vůbec, ale raději se zeptat počítače, že váš program mluví s tím, co vidí IP adresu vašeho počítače. např. Pokud píšete klientský program, odešlete na server zprávu s žádostí, aby server odeslal jako data adresu IP, ze které pochází váš požadavek. Tímto způsobem budete vědět, co je relevantní IP adresa, vzhledem k kontextu počítače, se kterým komunikujete.

To znamená, že tento trik nemusí být vhodný pro některé účely (např. Když nekomunikujete s konkrétním počítačem), takže někdy potřebujete pouze shromáždit seznam všech IP adres spojených s vaším počítačem. Nejlepším způsobem, jak toho dosáhnout v rámci Unixu/Macu (AFAIK), je volání getifaddrs () a iterace výsledků. V systému Windows zkuste GetAdaptersAddresses () získat podobné funkce. Například použití obou, viz funkce GetNetworkInterfaceInfos () v tento soubor .

31
Jeremy Friesner

Problém se všemi přístupy založenými na gethostbyname spočívá v tom, že nedostanete všechny IP adresy přiřazené konkrétnímu počítači. Servery mají obvykle více než jeden adaptér.

Zde je příklad, jak můžete iterovat prostřednictvím všech adres Ipv4 a Ipv6 na hostitelském počítači:

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

Můžete použít gethostname následovaný gethostbyname, abyste získali interní IP adresu vašeho lokálního rozhraní. 

Tato vrácená IP adresa se však může lišit od vaší externí IP adresy. Pro získání externí IP adresy budete muset komunikovat s externím serverem, který vám sdělí, jaká je vaše externí IP adresa. Protože externí IP není vaše, ale jsou to vaše směrovače.

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

Můžete také vždy použít pouze program 127.0.0.1, který vždy reprezentuje lokální počítač.

Maska podsítě ve Windows:

Masku podsítě (a brány a další informace) můžete získat dotazováním podklíče této položky registru:

HKEY_LOCAL_MACHINE SYSTÉM CurrentControlSet Služby cpip Parametry Rozhraní

Vyhledejte hodnotu registru SubnetMask.

Další metody pro získání informací o rozhraní v systému Windows:

Informace, které hledáte, můžete také získat pomocí: WSAIoctl s touto volbou: SIO_GET_INTERFACE_LIST 

15
Brian R. Bondy

Nelze to provést ve standardním C++.

Vysílám to, protože je to jediná správná odpověď. Vaše otázka se ptá, jak to udělat v C++. Nemůžete to udělat v C++. Můžete to udělat ve Windows, POSIXu, Linuxu, Androidu, ale všechny tyto OS-specifické řešení a nejsou součástí jazykového standardu.

Standard C++ nemá síťovou vrstvu vůbec.

Předpokládám, že máte tento nesprávný předpoklad, že C++ Standard definuje stejný rozsah funkcí jako ostatní jazykové standardy Java. Zatímco Java může mít vestavěné síťové rozhraní (a dokonce i rámec GUI) ve vlastní jazykové knihovně, C++ ne.

I když existují API a knihovny třetích stran, které mohou být používány programem C++, není to v žádném případě totéž, co říkáte, že to můžete udělat v C++.

Zde je příklad objasnění toho, co myslím. Soubor můžete otevřít v jazyce C++, protože má třídu fstream jako součást své standardní knihovny. Nejedná se o totéž, co používáte CreateFile(), což je funkce specifická pro Windows a dostupná pouze pro WINAPI.

6
sashoalm

Všimněte si také, že "místní IP" nemusí být obzvláště jedinečná věc. Pokud se nacházíte v několika fyzických sítích (např. Kabelové + bezdrátové + bluetooth, nebo server se spoustou ethernetových karet, atd.), Nebo máte nastavení rozhraní TAP/TUN, váš počítač může snadno mít celou řadu rozhraní. 

4
jakobengblom2

Jak získat IP adresu místního počítače v síti zdá se, že řešení docela dobře ...

3
PhiLho

Specifika Winsock:

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

z torial: Pokud používáte winsock, zde je způsob: http://tangentsoft.net/wskfaq/examples/ipaddr.html

Pokud jde o podsíťovou část otázky; Neexistuje platforma agnostický způsob, jak získat masku podsítě jako POSIX socket API (které všechny moderní operační systémy implementují) to nespecifikuje. Takže budete muset použít jakoukoli metodu, která je k dispozici na platformě, kterou používáte.

2
nymacro

Byl jsem schopen to provést pomocí služby DNS v rámci VS2013 s následujícím kódem:

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

Musel jsem přidat Dnsapi.lib jako závislost závislost v linker možnost.

Reference zde .

1
Zac

Navrhuji svůj kód.

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

V DEV C++ jsem použil čistý C s WIN32, s tímto daným kódem:

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;

Když kliknu na tlačítko 'ip', funguje to. Ale podruhé program skončí (bez varování nebo chyby). Když dělám:

//WSACleanup(); 

Program se neukončí, a to ani klepnutím na stejné tlačítko vícekrát s nejrychlejší rychlostí. Takže WSACleanup () nemusí dobře fungovat s Dev-C++.

0
Ivan

Nemůžeš prostě poslat na INADDR_BROADCAST ? Je pravda, že to bude posílat na všechna rozhraní - ale to je zřídka problém.

V opačném případě by ioctl a SIOCGIFBRDADDR měli získat adresu * nix a WSAioctl a SIO_GET_BROADCAST_ADDRESS na win32.

0
Mark Brackett