在C++中,获取本地计算机的IP地址和子网掩码的最简单方法是什么?
我希望能够在本地网络中检测本地计算机的IP地址.在我的特定情况下,我有一个子网掩码为255.255.255.0的网络,我的计算机的IP地址是192.168.0.5.我需要以编程方式获取这两个值,以便向我的网络发送广播消息(对于我的特定情况,格式为192.168.0.255)
编辑:许多答案没有给出我预期的结果,因为我有两个不同的网络IP.Torial的代码完成了这个伎俩(它给了我两个IP地址).谢谢.
编辑2:感谢Brian R. Bondy提供有关子网掩码的信息.
这个问题比它看起来更棘手,因为在许多情况下,没有"本地计算机的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()函数.
基于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(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! }
您可以使用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 \系统\ CurrentControlSet \服务\ TCPIP \参数\接口
查找注册表值SubnetMask.
在Windows中获取接口信息的其他方法:
您还可以使用以下选项检索您要查找的信息: WSAIoctl with this option:SIO_GET_INTERFACE_LIST
我发布这个是因为这是唯一正确的答案.您的问题询问如何在C++中执行此操作.好吧,你不能用C++做到这一点.您可以在Windows,POSIX,Linux,Android中执行此操作,但所有这些都是特定于操作系统的解决方案,而不是语言标准的一部分.
标准C++ 根本没有网络层.
我假设您有一个错误的假设,即C++ Standard定义了与其他语言标准Java相同的功能范围.虽然Java可能在语言自己的标准库中具有内置网络(甚至是GUI框架),但C++却没有.
虽然C++程序可以使用第三方API和库,但这与使用C++实现它并不相同.
这是一个澄清我的意思的例子.您可以使用C++打开文件,因为它有一个fstream
类作为其标准库的一部分.这与使用不同CreateFile()
,后者是Windows特有的功能,仅适用于WINAPI.