C++ 获取本地计算机的IP地址
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/122208/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Get the IP Address of local computer
提问by djeidot
In C++, what's the easiest way to get the local computer's IP address and subnet mask?
在 C++ 中,获取本地计算机的 IP 地址和子网掩码的最简单方法是什么?
I want to be able to detect the local machine's IP address in my local network. In my particular case, I have a network with a subnet mask of 255.255.255.0 and my computer's IP address is 192.168.0.5. I need to get these had two values programmatically in order to send a broadcast message to my network (in the form 192.168.0.255, for my particular case)
我希望能够在我的本地网络中检测到本地机器的 IP 地址。在我的特殊情况下,我有一个子网掩码为 255.255.255.0 的网络,我的计算机的 IP 地址为 192.168.0.5。我需要以编程方式获得这些有两个值,以便向我的网络发送广播消息(对于我的特殊情况,格式为 192.168.0.255)
Edit: Many answers were not giving the results I expected because I had two different network IP's. Torial's code did the trick (it gave me both IP addresses). Thanks.
编辑:许多答案没有给出我预期的结果,因为我有两个不同的网络 IP。Torial的代码成功了(它给了我两个 IP 地址)。谢谢。
Edit 2: Thanks to Brian R. Bondyfor the info about the subnet mask.
编辑 2:感谢Brian R. Bondy提供有关子网掩码的信息。
回答by Jeremy Friesner
The question is trickier than it appears, because in many cases there isn't "an IP address for the local computer" so much as a number of different IP addresses. For example, the Mac I'm typing on right now (which is a pretty basic, standard Mac setup) has the following IP addresses associated with it:
这个问题比看起来更棘手,因为在许多情况下,“本地计算机的 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
... and it's not just a matter of figuring out which of the above is "the real IP address", either... they are all "real" and useful; some more useful than others depending on what you are going to use the addresses for.
……这不仅仅是弄清楚上面哪个是“真实IP地址”的问题,或者……它们都是“真实的”和有用的;一些比其他更有用取决于您将使用地址的目的。
In my experience often the best way to get "an IP address" for your local computer is not to query the local computer at all, but rather to ask the computer your program is talking to what it sees your computer's IP address as. e.g. if you are writing a client program, send a message to the server asking the server to send back as data the IP address that your request came from. That way you will know what the relevantIP address is, given the context of the computer you are communicating with.
根据我的经验,获得本地计算机“IP 地址”的最佳方法通常根本不是查询本地计算机,而是询问您的程序正在与之通信的计算机,它认为您的计算机的 IP 地址是什么。例如,如果您正在编写客户端程序,请向服务器发送一条消息,要求服务器将您的请求来自的 IP 地址作为数据发回。考虑到您正在与之通信的计算机的上下文,这样您就会知道相关的IP 地址是什么。
That said, that trick may not be appropriate for some purposes (e.g. when you're not communicating with a particular computer) so sometimes you just need to gather the list of all the IP addresses associated with your machine. The best way to do that under Unix/Mac (AFAIK) is by calling getifaddrs() and iterating over the results. Under Windows, try GetAdaptersAddresses() to get similar functionality. For example usages of both, see the GetNetworkInterfaceInfos() function in this file.
也就是说,该技巧可能不适用于某些目的(例如,当您不与特定计算机通信时),因此有时您只需要收集与您的机器相关联的所有 IP 地址的列表。在 Unix/Mac (AFAIK) 下执行此操作的最佳方法是调用 getifaddrs() 并迭代结果。在 Windows 下,尝试 GetAdaptersAddresses() 以获得类似的功能。有关两者的用法示例,请参阅此文件中的 GetNetworkInterfaceInfos() 函数。
回答by kgriffs
The problem with all the approaches based on gethostbyname is that you will not get all IP addresses assigned to a particular machine. Servers usually have more than one adapter.
所有基于 gethostbyname 的方法的问题在于,您不会获得分配给特定机器的所有 IP 地址。服务器通常有多个适配器。
Here is an example of how you can iterate through all Ipv4 and Ipv6 addresses on the host machine:
下面是一个示例,说明如何遍历主机上的所有 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!
}
回答by Brian R. Bondy
You can use gethostname followed by gethostbyname to get your local interface internal IP.
您可以使用 gethostname 后跟 gethostbyname 来获取本地接口内部 IP。
This returned IP may be different from your external IP though. To get your external IP you would have to communicate with an external server that will tell you what your external IP is. Because the external IP is not yours but it is your routers.
不过,这个返回的 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;
}
You can also always just use 127.0.0.1 which represents the local machine always.
您也可以始终只使用 127.0.0.1,它始终代表本地机器。
Subnet mask in Windows:
Windows 中的子网掩码:
You can get the subnet mask (and gateway and other info) by querying subkeys of this registry entry:
您可以通过查询此注册表项的子项来获取子网掩码(以及网关和其他信息):
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces
Look for the registry value SubnetMask.
查找注册表值 SubnetMask。
Other methods to get interface information in Windows:
在 Windows 中获取接口信息的其他方法:
You could also retrieve the information you're looking for by using: WSAIoctlwith this option: SIO_GET_INTERFACE_LIST
您还可以使用以下选项检索您要查找的信息: WSAIoctl:SIO_GET_INTERFACE_LIST
回答by sashoalm
You cannot do that in Standard C++.
你不能在标准 C++ 中做到这一点。
I'm posting this because it is the only correct answer. Your question asks how to do it in C++. Well, you can't do it in C++. You can do it in Windows, POSIX, Linux, Android, but all those are OS-specific solutionsand not part of the language standard.
我发布这个是因为它是唯一正确的答案。您的问题询问如何在 C++ 中做到这一点。好吧,你不能在 C++ 中做到这一点。您可以在 Windows、POSIX、Linux、Android 中执行此操作,但所有这些都是特定于操作系统的解决方案,而不是语言标准的一部分。
Standard C++ does not have a networking layerat all.
标准 C++根本没有网络层。
I assume you have this wrong assumption that C++ Standard defines the same scope of features as other language standards, Java. While Java might have built-in networking (and even a GUI framework) in the language's own standard library, C++ does not.
我假设您有一个错误的假设,即 C++ 标准定义了与其他语言标准 Java 相同的特性范围。虽然 Java 可能在语言自己的标准库中内置了网络(甚至是 GUI 框架),但 C++ 没有。
While there are third-party APIs and libraries which can be used by a C++ program, this is in no way the same as saying that you can do it in C++.
尽管存在可供 C++ 程序使用的第三方 API 和库,但这与说您可以在 C++ 中执行完全不同。
Here is an example to clarify what I mean. You can open a file in C++ because it has an fstream
class as part of its standard library. This is not the same thing as using CreateFile()
, which is a Windows-specific function and available only for WINAPI.
这是一个例子来阐明我的意思。您可以在 C++ 中打开文件,因为它fstream
的标准库中有一个类。这与 using 不同CreateFile()
,后者是 Windows 特定的函数,仅适用于 WINAPI。
回答by jakobengblom2
Also, note that "the local IP" might not be a particularly unique thing. If you are on several physical networks (wired+wireless+bluetooth, for example, or a server with lots of Ethernet cards, etc.), or have TAP/TUN interfaces setup, your machine can easily have a whole host of interfaces.
另请注意,“本地 IP”可能不是特别独特的东西。如果您在多个物理网络上(例如,有线+无线+蓝牙,或具有大量以太网卡的服务器等),或者设置了 TAP/TUN 接口,您的机器可以轻松拥有大量接口。
回答by GEOCHET
Winsock specific:
温索克具体:
// 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();
回答by nymacro
from torial: If you use winsock, here's a way: http://tangentsoft.net/wskfaq/examples/ipaddr.html
来自 torial:如果你使用 winsock,这里有一个方法:http: //tangentsoft.net/wskfaq/examples/ipaddr.html
As for the subnet portion of the question; there is not platform agnostic way to retrieve the subnet mask as the POSIX socket API (which all modern operating systems implement) does not specify this. So you will have to use whatever method is available on the platform you are using.
至于问题的子网部分;没有平台无关的方式来检索子网掩码,因为 POSIX 套接字 API(所有现代操作系统都实现)没有指定这一点。因此,您必须使用您所使用的平台上可用的任何方法。
回答by PhiLho
How to Obtain the IP Address of the Local Machine on the Networkseems to describe the solution quite well...
如何获取网络上本地机器的IP地址似乎很好地描述了解决方案......
回答by Zac
I was able to do it using DNS service under VS2013 with the following code:
我能够在 VS2013 下使用 DNS 服务使用以下代码来做到这一点:
#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);
I had to add Dnsapi.lib as addictional dependency in linker option.
我不得不在链接器选项中添加 Dnsapi.lib 作为依赖。
Reference here.
参考这里。
回答by Mark Yang
I suggest my code.
我建议我的代码。
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; }