获取机器的IP地址

这个问题与之前提出的获取本地计算机的IP地址 -Question几乎相同。 但是我需要findLinux机器的IP地址。

所以:我如何 – 在C ++中编程方式 – 检测我的应用程序正在运行的Linux服务器的IP地址。 服务器将至less有两个IP地址,我需要一个特定的(在给定的networking(公众之一))。

我确定有一个简单的function来做到这一点 – 但是在哪里?


为了使事情更清楚:

  • 服务器显然会有“localhost”:127.0.0.1
  • 服务器将有一个内部(pipe理)IP地址:172.16.xx
  • 服务器将有一个外部(公共)IP地址:80.190.xx

我需要find外部IP地址绑定我的应用程序。 显然我也可以绑定到INADDR_ANY(实际上这就是我现在所做的)。 不过,我宁愿检测公共地址。

我发现OS X上的ioctl解决scheme是有问题的(这是POSIX兼容,所以应该类似​​于Linux)。 然而,getifaddress()会让你轻松地做同样的事情,它在os x 10.5上可以正常工作,下面应该是一样的。

我已经做了一个快速的例子,下面将打印所有的机器的IPv4地址,(你也应该检查getifaddrs是成功的,即返回0)。

我已经更新了它也显示IPv6地址。

 #include <stdio.h> #include <sys/types.h> #include <ifaddrs.h> #include <netinet/in.h> #include <string.h> #include <arpa/inet.h> int main (int argc, const char * argv[]) { struct ifaddrs * ifAddrStruct=NULL; struct ifaddrs * ifa=NULL; void * tmpAddrPtr=NULL; getifaddrs(&ifAddrStruct); for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) { if (!ifa->ifa_addr) { continue; } if (ifa->ifa_addr->sa_family == AF_INET) { // check it is IP4 // is a valid IP4 Address tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; char addressBuffer[INET_ADDRSTRLEN]; inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN); printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); } else if (ifa->ifa_addr->sa_family == AF_INET6) { // check it is IP6 // is a valid IP6 Address tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; char addressBuffer[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN); printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); } } if (ifAddrStruct!=NULL) freeifaddrs(ifAddrStruct); return 0; } 
  1. 创build一个套接字。
  2. 执行ioctl(<socketfd>, SIOCGIFCONF, (struct ifconf)&buffer);

有关ifconfifreq结构的信息,请阅读/usr/include/linux/if.h 。 这应该给你在系统上的每个接口的IP地址。 另外请阅读/usr/include/linux/sockios.h了解更多的ioctls。

我喜欢jjvainio的答案 。 正如Zan Lnyx所说,它使用本地路由表来查找将用于连接到特定外部主机的以太网接口的IP地址。 通过使用连接的UDP套接字,您可以获取信息而不实际发送任何数据包。 该方法要求您select特定的外部主机。 大多数时候,任何知名的公共知识产权都应该做到这一点。 我喜欢Google的公共DNS服务器地址8.8.8.8,但是可能有些时候您想要select不同的外部主机IP。 以下是一些说明完整方法的代码。

 void GetPrimaryIp(char* buffer, size_t buflen) { assert(buflen >= 16); int sock = socket(AF_INET, SOCK_DGRAM, 0); assert(sock != -1); const char* kGoogleDnsIp = "8.8.8.8"; uint16_t kDnsPort = 53; struct sockaddr_in serv; memset(&serv, 0, sizeof(serv)); serv.sin_family = AF_INET; serv.sin_addr.s_addr = inet_addr(kGoogleDnsIp); serv.sin_port = htons(kDnsPort); int err = connect(sock, (const sockaddr*) &serv, sizeof(serv)); assert(err != -1); sockaddr_in name; socklen_t namelen = sizeof(name); err = getsockname(sock, (sockaddr*) &name, &namelen); assert(err != -1); const char* p = inet_ntop(AF_INET, &name.sin_addr, buffer, buflen); assert(p); close(sock); } 

正如你发现,没有一个单一的“本地IP地址”。 以下是如何找出可以发送到特定主机的本地地址。

  1. 创build一个UDP套接字
  2. 将套接字连接到外部地址(最终将接收本地地址的主机)
  3. 使用getsockname来获取本地地址

这样做的好处是可以处理许多unixtypes的代码,而且你可以修改它以适应任何操作系统。 上面的所有解决scheme都给我编译器的错误取决于月亮的相位。 现在有一个很好的POSIX方法来做到这一点…不要使用这个(在写这个的时候,情况并非如此)。

 // ifconfig | perl -ne 'print "$1\n" if /inet addr:([\d.]+)/' #include <stdlib.h> int main() { setenv("LANG","C",1); FILE * fp = popen("ifconfig", "r"); if (fp) { char *p=NULL, *e; size_t n; while ((getline(&p, &n, fp) > 0) && p) { if (p = strstr(p, "inet ")) { p+=5; if (p = strchr(p, ':')) { ++p; if (e = strchr(p, ' ')) { *e='\0'; printf("%s\n", p); } } } } } pclose(fp); return 0; } 

关于Steve Baker的说法,可以在netdevice(7)手册页中findSIOCGIFCONF ioctl的描述。

一旦获得了主机上所有IP地址的列表,就必须使用特定于应用程序的逻辑来筛选出你不想要的地址,并希望剩下一个IP地址。

不要硬编码:这是可以改变的东西。 许多程序通过读取configuration文件找出要绑定的内容,并执行所说的任何操作。 这样,如果将来某个时候您的程序需要绑定到不是公有IP的程序,那么可以这样做。

我不认为你的问题有一个确定的正确答案。 相反,有一大堆如何接近你想要的方法。 因此,我将提供一些提示如何完成。

如果一台机器有两个以上的接口( lo计为一个),那么您将很容易自动检测到正确的接口。 这里有一些关于如何做的食谱。

例如,如果主机位于NAT防火墙后面的DMZ中,将公共IP转换为某个私有IP并转发请求,则会出现问题。 您的机器可能有10个接口,但只有一个对应于公共接口。

即使自动检测function也不起作用,因为在双NAT中,防火墙甚至可以将源IP转换成完全不同的东西。 所以你甚至不能确定,默认路由导致你的接口与公共接口。

通过默认路由进行检测

这是我推荐的自动检测方式

ip r get 1.1.1.1通常告诉你有默认路由的接口。

如果你想用你最喜欢的脚本/编程语言重新创build这个,使用strace ip r get 1.1.1.1并按照黄砖路。

/etc/hosts设置它

如果你想保持控制,这是我的build议

你可以在/etc/hosts创build一个条目

 80.190.1.3 publicinterfaceip 

然后你可以使用这个别名publicinterfaceip来引用你的公共接口。

可悲的是, haproxy并不喜欢IPv6的这个技巧

使用环境

如果您不是root这是/etc/hosts一个很好的解决方法

/etc/hosts相同。 但为此使用环境。 你可以试试/etc/profile~/.profile

因此,如果你的程序需要一个variablesMYPUBLICIP那么你可以包括像这样的代码(这是C,随便从它创buildC ++):

 #define MYPUBLICIPENVVAR "MYPUBLICIP" const char *mypublicip = getenv(MYPUBLICIPENVVAR); if (!mypublicip) { fprintf(stderr, "please set environment variable %s\n", MYPUBLICIPENVVAR); exit(3); } 

所以你可以像这样调用你的脚本/程序/path/to/your/script

MYPUBLICIP=80.190.1.3 /path/to/your/script

这甚至可以在crontab

枚举所有接口并消除那些你不想要的

绝望的方式,如果你不能使用ip

如果你知道你不想要的东西,你可以枚举所有的接口,并忽略所有的错误。

对于这种方法,这里似乎已经是https://stackoverflow.com/a/265978/490291的回答了。;

像DLNA一样

醉酒男子试图酗酒的方式

你可以尝试枚举你的networking上的所有UPnP网关,这样就找出了一些适合某些“外部”事物的path。 这甚至可能在您的默认路线不指向的路线上。

有关更多信息,请参阅https://en.wikipedia.org/wiki/Internet_Gateway_Device_Protocol

这给你一个良好的印象,哪一个是你真正的公共界面,即使你的默认路线指向其他地方。

还有更多

山与先知相遇的地方

IPv6路由器通告自己给你正确的IPv6前缀。 看前缀给你一个暗示,如果它有一些内部IP或全球性的。

您可以侦听IGMP或IBGP帧来找出合适的网关。

有less于2 ^ 32个IP地址。 因此,在局域网上花费很长时间才能完成所有的工作。 从您的angular度来看,这给了您关于大多数互联网所在位置的统计提示。 不过,你应该比着名的https://de.wikipedia.org/wiki/SQL_Slammer更明智些;

ICMP甚至ARP是networking边带信息的良好来源。 它也可以帮助你。

您可以使用以太网广播地址来联系您的所有networking基础设施设备,这些设备通常可以提供帮助,如DHCP(甚至DHCPv6)等。

这个附加列表可能是无穷无尽的,并且总是不完整的,因为每个networking设备制造商都忙于在如何自动检测自己的设备上发现新的安全漏洞。 这对于如何检测一些没有的公共接口常常有帮助。

“Nuff说。 出。

你可以用curl做一些整合,就像: curl www.whatismyip.org从shell会得到你的全局ip。 你有点依赖于某个外部服务器,但是如果你在一个NAT之后,你将永远是这样。

由于这个问题指定了Linux,我发现一台机器的IP地址最喜欢的技术是使用netlink。 通过创buildNETLINK_ROUTE协议的netlink套接字并发送RTM_GETADDR,应用程序将收到一个包含所有可用IP地址的消息。 这里提供了一个例子。

为了简单地处理消息的一部分,libmnl很方便。 如果你对搞清楚NETLINK_ROUTE(以及如何parsing)的不同选项有兴趣,最好的源代码是iproute2的源代码 (特别是监视器应用程序)以及内核中的接收函数。 rtnetlink的手册页也包含有用的信息。

一个优雅的Linux脚本解决scheme可以在http://www.damnsmalllinux.org/f/topic-3-23-17031-0.html

// Use a HTTP request to a well known server that echo's back the public IP address void GetPublicIP(CString & csIP) { // Initialize COM bool bInit = false; if (SUCCEEDED(CoInitialize(NULL))) { // COM was initialized bInit = true;

  // Create a HTTP request object MSXML2::IXMLHTTPRequestPtr HTTPRequest; HRESULT hr = HTTPRequest.CreateInstance("MSXML2.XMLHTTP"); if (SUCCEEDED(hr)) { // Build a request to a web site that returns the public IP address VARIANT Async; Async.vt = VT_BOOL; Async.boolVal = VARIANT_FALSE; CComBSTR ccbRequest = L"http://whatismyipaddress.com/"; // Open the request if (SUCCEEDED(HTTPRequest->raw_open(L"GET",ccbRequest,Async))) { // Send the request if (SUCCEEDED(HTTPRequest->raw_send())) { // Get the response CString csRequest = HTTPRequest->GetresponseText(); // Parse the IP address CString csMarker = "<!-- contact us before using a script to get your IP address -->"; int iPos = csRequest.Find(csMarker); if (iPos == -1) return; iPos += csMarker.GetLength(); int iPos2 = csRequest.Find(csMarker,iPos); if (iPos2 == -1) return; // Build the IP address int nCount = iPos2 - iPos; csIP = csRequest.Mid(iPos,nCount); } } } } // Unitialize COM if (bInit) CoUninitialize(); 

}