Получить 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, ваш компьютер может легко иметь целый ряд интерфейсов.
Ответ 5
Как получить IP-адрес локальной машины в сети, похоже, хорошо описывает решение...
Ответ 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" );
Конец не стоит беспокоиться:)