此问题与之前询问的获取本地计算机的IP地址 -Question 几乎相同.但是,我需要找到Linux机器的IP地址.
那么:我如何 - 在C++中以编程方式 - 检测运行我的应用程序的linux服务器的IP地址.服务器至少有两个IP地址,我需要一个特定的IP地址(给定网络中的一个(公共网络)).
我确信有一个简单的功能可以做到这一点 - 但在哪里?
为了让事情更清楚:
服务器显然会有"localhost":127.0.0.1
服务器将具有内部(管理)IP地址:172.16.xx
服务器将具有外部(公共)IP地址:80.190.xx
我需要找到外部IP地址来绑定我的应用程序.显然我也可以绑定到INADDR_ANY(实际上这就是我现在所做的).不过,我更愿意检测公共地址.
我发现ioctl解决方案在os x上有问题(符合POSIX,所以应该与linux类似).但是getifaddress()会让你轻松地做同样的事情,它在os x 10.5上对我来说很好,下面应该是一样的.
我在下面做了一个快速示例,它将打印机器的所有IPv4地址,(您还应检查getifaddrs是否成功,即返回0).
我已更新它显示IPv6地址.
#include#include #include #include #include #include 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; }
创建一个套接字.
演出 ioctl(
阅读/usr/include/linux/if.h
有关ifconf
和ifreq
结构的信息.这应该为您提供系统上每个接口的IP地址.另请阅读/usr/include/linux/sockios.h
其他ioctls.
我喜欢jjvainio的回答.正如Zan Lnyx所说,它使用本地路由表来查找将用于连接到特定外部主机的以太网接口的IP地址.通过使用连接的UDP套接字,您可以在不实际发送任何数据包的情况下获取信息.该方法要求您选择特定的外部主机.大多数时候,任何知名的公共知识产权都应该做到这一点.出于此目的,我喜欢Google的公共DNS服务器地址8.8.8.8,但有时您可能希望选择不同的外部主机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地址".以下是如何找出可以发送到特定主机的本地地址.
创建UDP套接字
将套接字连接到外部地址(最终将接收本地地址的主机)
使用getsockname获取本地地址
这样做的好处是可以处理多种版本的unix ...而且你可以轻松修改它以适用于任何o/s.上面的所有解决方案都会根据月球的相位给出编译器错误.有一个很好的POSIX方式去做的那一刻......不要使用它(在写这个时,情况并非如此).
// ifconfig | perl -ne 'print "$1\n" if /inet addr:([\d.]+)/' #includeint 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; }
我认为您的问题没有确定的正确答案。相反,有很多方法可以使您接近自己想要的东西。因此,我将提供一些提示如何完成它。
如果一台机器有两个以上的接口(lo
算作一个),您将很难自动检测正确的接口。这里有一些如何做的食谱。
例如,问题在于主机是否位于NAT防火墙后面的DMZ中,该防火墙将公共IP更改为某些私有IP并转发请求。您的计算机可能有10个接口,但是只有一个对应于公共接口。
如果您使用双NAT,则即使自动检测也不起作用,在这种情况下,防火墙甚至会将源IP转换为完全不同的内容。因此,您甚至无法确定默认路由会导致您的接口带有公共接口。
通过默认路由检测到这是我推荐的自动检测事物的方法
喜欢的东西ip r get 1.1.1.1
通常会告诉你它具有默认路由的接口。
如果要用自己喜欢的脚本/编程语言重新创建它,请使用strace ip r get 1.1.1.1
并遵循黄色的砖砌路。
/etc/hosts
如果您想保持控制,这是我的建议
您可以在/etc/hosts
like中创建一个条目
80.190.1.3 publicinterfaceip
然后,您可以使用该别名publicinterfaceip
来引用您的公共接口。
使用环境可悲的是
haproxy
,IPv6无法解决这个问题
这是一个很好的解决方法
/etc/hosts
,以防您不root
与相同/etc/hosts
。但要为此使用环境。您可以尝试/etc/profile
或~/.profile
为此。
因此,如果您的程序需要一个变量,MYPUBLICIP
则可以包含以下代码(这是C,请随时从中创建C ++):
#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
如果确实知道不需要的内容,则可以枚举所有接口,而忽略所有错误的接口。
对于这种方法,这里似乎已经是一个答案/sf/ask/17360801/。
像DLNA一样醉酒的男人想把自己淹死在酒精中的方式
您可以尝试枚举网络上的所有UPnP网关,并以此方式为某些“外部”事物找到正确的路由。甚至可能在默认路由未指向的路由上。
有关此内容的更多信息,请参见https://en.wikipedia.org/wiki/Internet_Gateway_Device_Protocol
即使您的默认路由指向其他位置,也可以给您一个很好的印象,即哪个是您真正的公共接口。
还有更多山与先知相遇的地方
IPv6路由器会自我宣传,为您提供正确的IPv6前缀。查看前缀可以提示您它是否具有一些内部IP或全局IP。
您可以侦听IGMP或IBGP帧,以找到合适的网关。
IP地址少于2 ^ 32。因此,在局域网上仅对它们全部执行ping操作并不需要很长时间。从您的角度来看,这可以为您提供有关Internet大部分位置的统计提示。但是,您应该比著名的https://de.wikipedia.org/wiki/SQL_Slammer更加明智。
ICMP甚至ARP是网络边带信息的良好来源。它也可能对您有帮助。
您可以使用以太网广播地址与您经常使用的所有网络基础设施设备进行联系,例如DHCP(甚至DHCPv6)等等。
这个额外的列表可能是无止境的,而且总是不完整的,因为每个网络设备制造商都在忙于发明如何自动检测自己的设备的新安全漏洞。这通常对如何检测不应该存在的公共接口很有帮助。
纳夫说。出来