Obtener la dirección IP de la computadora local

En C ++, ¿cuál es la forma más fácil de obtener la dirección IP y la máscara de subred de la computadora local?

Quiero poder detectar la dirección IP de la máquina local en mi red local. En mi caso particular, tengo una red con una máscara de subred de 255.255.255.0 y la dirección IP de mi computadora es 192.168.0.5. Necesito obtener estos dos valores programáticamente para enviar un mensaje de difusión a mi red (en el formulario 192.168.0.255, para mi caso particular)

Editar: Muchas respuestas no dieron los resultados que esperaba porque tenía dos IP de red diferentes. El código de Torial hizo el truco (me dio ambas direcciones IP). Gracias.

Editar 2: Gracias a Brian R. Bondy por la información sobre la máscara de subred.

La pregunta es más complicada de lo que parece, porque en muchos casos no hay “una dirección IP para la computadora local” sino una cantidad de direcciones IP diferentes. Por ejemplo, la Mac en la que estoy escribiendo ahora (que es una configuración de Mac estándar muy básica) tiene las siguientes direcciones IP asociadas a ella:

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 

… y no se trata solo de averiguar cuál de los anteriores es “la dirección IP real”, tampoco … todos son “reales” y útiles; algunos más útiles que otros dependiendo de para qué vas a usar las direcciones.

En mi experiencia, a menudo la mejor manera de obtener “una dirección IP” para su computadora local no es consultar la computadora local, sino preguntarle a la computadora que su progtwig está hablando con lo que ve en la dirección IP de su computadora. por ejemplo, si está escribiendo un progtwig de cliente, envíe un mensaje al servidor pidiéndole al servidor que envíe como datos la dirección IP de la que proviene su solicitud. De esta forma sabrá cuál es la dirección IP relevante , dado el contexto de la computadora con la que se está comunicando.

Dicho esto, ese truco puede no ser apropiado para algunos fines (por ejemplo, cuando no se está comunicando con una computadora en particular), por lo que a veces solo necesita reunir la lista de todas las direcciones IP asociadas a su máquina. La mejor manera de hacerlo en Unix / Mac (AFAIK) es llamando a getifaddrs () e iterando sobre los resultados. En Windows, pruebe GetAdaptersAddresses () para obtener una funcionalidad similar. Por ejemplo, los usos de ambos, vea la función GetNetworkInterfaceInfos () en este archivo .

El problema con todos los enfoques basados ​​en gethostbyname es que no obtendrá todas las direcciones IP asignadas a una máquina en particular. Los servidores generalmente tienen más de un adaptador.

Aquí hay un ejemplo de cómo puede iterar a través de todas las direcciones Ipv4 e Ipv6 en la máquina host:

 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(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(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! } 

Puede usar gethostname seguido de gethostbyname para obtener su IP interna de la interfaz local.

Sin embargo, esta IP devuelta puede ser diferente de su IP externa. Para obtener su IP externa, tendría que comunicarse con un servidor externo que le dirá cuál es su IP externa. Porque la IP externa no es tuya, pero son tus enrutadores.

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

También siempre puede usar 127.0.0.1 que representa la máquina local siempre.

Máscara de subred en Windows:

Puede obtener la máscara de subred (y la puerta de enlace y otra información) consultando las subclaves de esta entrada de registro:

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

Busque el valor de registro SubnetMask.

Otros métodos para obtener información de interfaz en Windows:

También puede recuperar la información que está buscando utilizando: WSAIoctl con esta opción: SIO_GET_INTERFACE_LIST

No puedes hacer eso en Estándar C ++.

Estoy publicando esto porque es la única respuesta correcta. Su pregunta le pregunta cómo hacerlo en C ++. Bueno, no puedes hacerlo en C ++. Puede hacerlo en Windows, POSIX, Linux, Android, pero todas esas son soluciones específicas de sistema operativo y no forman parte del estándar de lenguaje.

El estándar C ++ no tiene una capa de red en absoluto.

Supongo que tiene esta suposición equivocada de que C ++ Standard define el mismo scope de funciones que otros estándares de lenguaje, Java. Si bien Java puede tener una red integrada (e incluso un marco de GUI) en la biblioteca estándar propia del lenguaje, C ++ no lo hace.

Si bien hay API y bibliotecas de terceros que pueden ser utilizadas por un progtwig C ++, esto de ninguna manera es lo mismo que decir que puede hacerlo en C ++.

Aquí hay un ejemplo para aclarar a qué me refiero. Puede abrir un archivo en C ++ porque tiene una clase fstream como parte de su biblioteca estándar. Esto no es lo mismo que usar CreateFile() , que es una función específica de Windows y está disponible solo para WINAPI.

Además, tenga en cuenta que “la IP local” podría no ser una cosa particularmente única. Si se encuentra en varias redes físicas (cableadas + inalámbricas + bluetooth, por ejemplo, o un servidor con muchas tarjetas Ethernet, etc.) o tiene configuradas las interfaces TAP / TUN, su máquina puede tener fácilmente una gran cantidad de interfaces.

Cómo obtener la dirección IP de la máquina local en la red parece describir la solución bastante bien …

Winsock específico:

 // 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(); 

de torial: si usa winsock, aquí hay una forma: http://tangentsoft.net/wskfaq/examples/ipaddr.html

En cuanto a la parte de la subred de la pregunta; no hay una forma de agnóstico de plataforma para recuperar la máscara de subred ya que la API de socket POSIX (que implementan todos los sistemas operativos modernos) no especifica esto. Por lo tanto, deberá usar cualquier método disponible en la plataforma que esté utilizando.

Pude hacerlo utilizando el servicio DNS bajo VS2013 con el siguiente código:

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

Tuve que agregar Dnsapi.lib como dependencia adiccional en la opción del enlazador.

Referencia aquí .

¿No puedes enviarlo a INADDR_BROADCAST ? Es cierto que eso enviará todas las interfaces, pero eso rara vez es un problema.

De lo contrario, ioctl y SIOCGIFBRDADDR deberían darle la dirección en * nix, y WSAioctl y SIO_GET_BROADCAST_ADDRESS en win32.

En DEV C ++, utilicé C puro con WIN32, con este fragmento de código:

 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; 

Cuando hago clic en el botón ‘mostrar ip’, funciona. Pero en la segunda vez, el progtwig se cierra (sin advertencia ni error). Cuando lo hago:

 //WSACleanup(); 

El progtwig no se cierra, incluso haciendo clic en el mismo botón varias veces con la velocidad más rápida. Entonces WSACleanup () puede no funcionar bien con Dev-C ++.

Sugiero mi código

 DllExport void get_local_ips(boost::container::vector& 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; }