Получить IP-адрес локального компьютера

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

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

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

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

Ответ 1

Вопрос сложнее, чем кажется, потому что во многих случаях не существует "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() в этом файле.

Ответ 2

Проблема со всеми подходами, основанными на 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!
}

Ответ 3

Вы можете использовать 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 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

Ответ 4

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

Ответ 6

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

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

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

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

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

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

Ответ 7

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

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

Ответ 8

Спецификация 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();

Ответ 9

Не можете ли вы просто отправить INADDR_BROADCAST? По общему признанию, это будет отправлять на всех интерфейсах, но это редко бывает проблемой.

В противном случае ioctl и SIOCGIFBRDADDR должны получить вам адрес на * nix и WSAioctl и SIO_GET_BROADCAST_ADDRESS на win32.

Ответ 10

Я смог сделать это с помощью службы 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 как зависимую зависимость в опции компоновщика.

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

Ответ 11

В DEV С++ я использовал чистый C с WIN32, с этим заданным фрагментом кода:

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;

Когда я нажимаю кнопку "show ip", она работает. Но во второй раз программа завершает работу (без предупреждения или ошибки). Когда я это сделаю:

//WSACleanup(); 

Программа не выходит, даже нажав одну и ту же кнопку несколько раз с максимальной скоростью. Поэтому WSACleanup() может не работать с Dev-С++..

Ответ 12

Самый простой способ!

Система ( "IPCONFIG" );

Конец не стоит беспокоиться:)