![](/img/trans.png)
[英]Linux / C++: Get Internet IP Address (not local computer's IP)
[英]How can I get the IP Address of a local computer?
在C++中,最简单的获取本地计算机的IP地址和su.net掩码的方法是什么?
我希望能够在我的 local.network 中检测到本地机器的 IP 地址。 在我的特殊情况下,我有一个 su.net 掩码为 255.255.255.0 的.network,我的计算机的 IP 地址是 192.168.0.5。 我需要以编程方式获得这两个值,以便向 my.network 发送广播消息(对于我的特殊情况,以 192.168.0.255 的形式)
编辑:许多答案没有给出我预期的结果,因为我有两个不同的网络 IP。 Torial的代码成功了(它给了我两个 IP 地址)。
编辑 2:感谢Brian R。Bondy提供有关 su.net 掩码的信息。
这个问题比看起来更棘手,因为在许多情况下,“本地计算机的 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<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!
}
您可以使用 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\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces
查找注册表值 SubnetMask。
在 Windows 中获取接口信息的其他方法:
您还可以使用以下选项检索您要查找的信息: WSAIoctl :SIO_GET_INTERFACE_LIST
我发布这个是因为它是唯一正确的答案。 您的问题询问如何在 C++ 中做到这一点。 好吧,你不能在 C++ 中做到这一点。 您可以在 Windows、POSIX、Linux、Android 中执行此操作,但所有这些都是特定于操作系统的解决方案,而不是语言标准的一部分。
标准 C++根本没有网络层。
我假设您有一个错误的假设,即 C++ 标准定义了与其他语言标准 Java 相同的特性范围。 虽然 Java 可能在语言自己的标准库中内置了网络(甚至是 GUI 框架),但 C++ 没有。
尽管存在可供 C++ 程序使用的第三方 API 和库,但这与说您可以在 C++ 中执行完全不同。
这是一个例子来阐明我的意思。 您可以在 C++ 中打开文件,因为它有一个fstream
类作为其标准库的一部分。 这与使用CreateFile()
,后者是 Windows 特定的函数,仅适用于 WINAPI。
温索克具体:
// 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();
另请注意,“本地 IP”可能不是特别独特的东西。 如果您在多个物理网络上(例如,有线+无线+蓝牙,或具有大量以太网卡的服务器等),或者设置了 TAP/TUN 接口,您的机器可以轻松拥有大量接口。
如何获取网络上本地机器的IP地址似乎很好地描述了解决方案......
来自 torial:如果你使用 winsock,这里有一个方法: http : //tangentsoft.net/wskfaq/examples/ipaddr.html
至于问题的子网部分; 没有平台无关的方式来检索子网掩码,因为 POSIX 套接字 API(所有现代操作系统都实现)没有指定这一点。 因此,您必须使用您所使用的平台上可用的任何方法。
我能够在 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);
我不得不在链接器选项中添加 Dnsapi.lib 作为依赖。
参考这里。
我建议我的代码。
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; }
此答案的修改版本。
添加了标头和库。
它也基于这些页面:
获取适配器地址
IP_ADAPTER_ADDRESSES_LH
IP_ADAPTER_UNICAST_ADDRESS_LH
总之,要获得一个IPv4地址,调用GetAdaptersAddresses()
来获取适配器,然后通过运行IP_ADAPTER_UNICAST_ADDRESS
结构开始FirstUnicastAddress
并获得Address
字段,然后将它与转换为可读格式inet_ntop()
以以下格式打印信息:
[ADAPTER]: Realtek PCIe
[NAME]: Ethernet 3
[IP]: 123.123.123.123
可以编译为:
cl test.cpp
或者,如果您需要在命令行中添加 libs 依赖项:
cl test.cpp Iphlpapi.lib ws2_32.lib
#include <winsock2.h>
#include <iphlpapi.h>
#include <stdio.h>
#include <ws2tcpip.h>
// Link with Iphlpapi.lib and ws2_32.lib
#pragma comment(lib, "Iphlpapi.lib")
#pragma comment(lib, "ws2_32.lib")
void ListIpAddresses() {
IP_ADAPTER_ADDRESSES* adapter_addresses(NULL);
IP_ADAPTER_ADDRESSES* adapter(NULL);
DWORD adapter_addresses_buffer_size = 16 * 1024;
// Get adapter addresses
for (int attempts = 0; attempts != 3; ++attempts) {
adapter_addresses = (IP_ADAPTER_ADDRESSES*) malloc(adapter_addresses_buffer_size);
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) {
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;
return;
}
}
// 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;
printf("[ADAPTER]: %S\n", adapter->Description);
printf("[NAME]: %S\n", adapter->FriendlyName);
// Parse all IPv4 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) {
SOCKADDR_IN* ipv4 = reinterpret_cast<SOCKADDR_IN*>(address->Address.lpSockaddr);
char str_buffer[16] = {0};
inet_ntop(AF_INET, &(ipv4->sin_addr), str_buffer, 16);
printf("[IP]: %s\n", str_buffer);
}
}
printf("\n");
}
free(adapter_addresses);
adapter_addresses = NULL;
}
int main() {
ListIpAddresses();
return 0;
}
你不能直接发送到INADDR_BROADCAST吗? 诚然,这将在所有接口上发送 - 但这很少成为问题。
否则,ioctl 和 SIOCGIFBRDADDR 应该为您提供 *nix 上的地址,以及 win32 上的WSAioctl 和 SIO_GET_BROADCAST_ADDRESS 。
在 DEV C++ 中,我将纯 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;
当我单击“显示 ip”按钮时,它起作用了。 但是第二次,程序退出(没有警告或错误)。 当我做:
//WSACleanup();
程序不会退出,甚至以最快的速度多次单击同一个按钮。 所以 WSACleanup() 可能不适用于 Dev-C++ ..
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.