Получить IP-адрес локального компьютера - PullRequest
47 голосов
/ 23 сентября 2008

В C ++, какой самый простой способ получить IP-адрес локального компьютера и маску подсети?

Я хочу иметь возможность определять IP-адрес локального компьютера в моей локальной сети. В моем конкретном случае у меня есть сеть с маской подсети 255.255.255.0, а IP-адрес моего компьютера - 192.168.0.5. Мне нужно, чтобы они имели два значения программно для отправки широковещательного сообщения в мою сеть (в форме 192.168.0.255, для моего конкретного случая)

Редактировать: Многие ответы не дали ожидаемых результатов, потому что у меня было два разных сетевых IP-адреса. Код Torial добился цели (он дал мне оба IP-адреса). Благодарю.

Редактировать 2: Спасибо Брайану Бонди за информацию о маске подсети.

Ответы [ 12 ]

31 голосов
/ 23 августа 2009

Вопрос сложнее, чем кажется, потому что во многих случаях «IP-адрес для локального компьютера» не столько, сколько количество IP-адресов. Например, Mac, на котором я сейчас печатаю (это довольно простая стандартная настройка Mac), имеет следующие 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

... и дело не только в том, чтобы выяснить, что из вышеперечисленного является "реальным IP-адресом", либо ... все они "настоящие" и полезные; некоторые более полезны, чем другие, в зависимости от того, для чего вы собираетесь использовать адреса.

По моему опыту, часто лучший способ получить "IP-адрес" для вашего локального компьютера - это вообще не запрашивать локальный компьютер, а спрашивать компьютер, с помощью которого ваша программа говорит об IP-адресе вашего компьютера , например если вы пишете клиентскую программу, отправьте на сервер сообщение с просьбой отправить обратно в качестве данных IP-адрес, с которого пришел ваш запрос. Таким образом, вы будете знать, что такое релевантный IP-адрес, учитывая контекст компьютера, с которым вы общаетесь.

Тем не менее, этот трюк может не подходить для некоторых целей (например, когда вы не общаетесь с конкретным компьютером), поэтому иногда вам просто нужно собрать список всех IP-адресов, связанных с вашей машиной. Лучший способ сделать это в Unix / Mac (AFAIK) - вызвать getifaddrs () и перебрать результаты. Под Windows попробуйте GetAdaptersAddresses (), чтобы получить аналогичную функциональность. Например, использование обоих, см. Функцию GetNetworkInterfaceInfos () в этот файл .

23 голосов
/ 31 мая 2012

Проблема всех подходов, основанных на gethostbyname, заключается в том, что вы не получите все IP-адреса, назначенные конкретной машине. Серверы обычно имеют более одного адаптера.

Вот пример того, как вы можете перебирать все адреса Ipv4 и Ipv6 на хост-машине:

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!
}
16 голосов
/ 23 сентября 2008

Вы можете использовать gethostname и gethostbyname для получения внутреннего IP-адреса вашего локального интерфейса.

Этот возвращенный IP-адрес может отличаться от вашего внешнего IP-адреса. Чтобы получить внешний IP-адрес, вам необходимо связаться с внешним сервером, который сообщит вам, каков ваш внешний IP-адрес. Потому что внешний IP не твой, а твои роутеры.

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

Вы также всегда можете просто использовать 127.0.0.1, который всегда представляет локальный компьютер.

Маска подсети в Windows:

Вы можете получить маску подсети (и шлюз и другую информацию), запросив подразделы этой записи реестра:

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

Найдите значение реестра SubnetMask.

Другие способы получения информации об интерфейсе в Windows:

Вы также можете получить информацию, которую ищете, используя: WSAIoctl с этим параметром: SIO_GET_INTERFACE_LIST

6 голосов
/ 04 марта 2015

Вы не можете сделать это в стандарте C ++.

Я публикую это, потому что это единственный правильный ответ. Ваш вопрос спрашивает, как это сделать в C ++. Ну, вы не можете сделать это в C ++. Вы можете сделать это в Windows, POSIX, Linux, Android, но все это ОС-ориентированные решения и не являются частью языкового стандарта.

Стандартный C ++ вообще не имеет сетевого уровня .

Полагаю, у вас неверное предположение, что Стандарт C ++ определяет тот же набор функций, что и другие стандарты языка, Java. В то время как Java может иметь встроенную сеть (и даже структуру GUI) в собственной стандартной библиотеке языка, C ++ этого не делает.

Хотя существуют сторонние API и библиотеки, которые могут использоваться программой на C ++, это ни в коем случае не означает, что вы можете сделать это в C ++.

Вот пример, чтобы уточнить, что я имею в виду. Вы можете открыть файл в C ++, потому что он имеет класс fstream как часть своей стандартной библиотеки. Это не то же самое, что использование CreateFile(), которое является специфичной для Windows функцией и доступно только для WINAPI.

4 голосов
/ 21 октября 2008

Кроме того, обратите внимание, что «локальный IP» может быть не особенно уникальным. Если вы находитесь в нескольких физических сетях (например, проводная + беспроводная + Bluetooth) или на сервере с большим количеством карт Ethernet и т. Д.) Или у вас настроены интерфейсы TAP / TUN, ваша машина может легко иметь целый ряд интерфейсов.

3 голосов
/ 23 сентября 2008

Как получить IP-адрес локального компьютера в сети , кажется, достаточно хорошо описывает решение ...

2 голосов
/ 23 сентября 2008

от ториала: Если вы используете winsock, вот способ: http://tangentsoft.net/wskfaq/examples/ipaddr.html

Что касается подсетевой части вопроса; не существует независимого от платформы способа получения маски подсети, так как API сокетов POSIX (который реализуют все современные операционные системы) не определяет это. Поэтому вам придется использовать любой доступный метод на платформе, которую вы используете.

2 голосов
/ 23 сентября 2008

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();
1 голос
/ 09 октября 2015

Я смог сделать это с помощью службы DNS под VS2013 со следующим кодом:

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

Мне пришлось добавить Dnsapi.lib в качестве зависимой зависимости в опции компоновщика.

Ссылка здесь .

0 голосов
/ 09 января 2018

Я предлагаю свой код.

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; }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...