如何在C中获取我的非环回网络ip地址?

如何在C中获取我的非环回网络ip地址?,c,networking,network-programming,communication,C,Networking,Network Programming,Communication,对于两台主机之间的通信,我需要将主机的IP地址发送到另一个站点。问题是,如果我请求我的IP地址,我可能会返回我的本地环回IP地址(127.x.x.x),而不是网络(以太网)IP地址 我使用以下代码: char myhostname[32]; gethostname(myhostname, 32); hp = gethostbyname(myhostname); unsigned my_ip = *(unsigned*)(hp->h_addr); if( (my_ip % 256) =

对于两台主机之间的通信,我需要将主机的IP地址发送到另一个站点。问题是,如果我请求我的IP地址,我可能会返回我的本地环回IP地址(127.x.x.x),而不是网络(以太网)IP地址

我使用以下代码:

char myhostname[32];


gethostname(myhostname, 32);
hp = gethostbyname(myhostname);
unsigned my_ip = *(unsigned*)(hp->h_addr);

if( (my_ip % 256) == 127) {
  /* Wrong IP adress as it's 127.x.x.x */
  printf("Error, local IP address!");
  return;
}
解决此问题的唯一方法是确保/etc/hosts中的主机名位于实际网络地址之后,而不是本地环回(例如Ubuntu的默认设置)

有没有一种方法可以在不依赖/etc/hosts内容的情况下解决这个问题

编辑:我更改了上面的代码,以便它使用getaddrinfo,但我仍然返回环回设备的编号(127.0,0,1),而不是实际的IP地址:

struct addrinfo hint = {0};
struct addrinfo *aip = NULL;
unsigned ip = 0;
struct sockaddr_in *sinp = NULL;

hint.ai_family = AF_INET; /* IPv4 */
hint.ai_socktype = SOCK_STREAM;

if(getaddrinfo(hostname, NULL, &hint, &aip) != 0) {
    return 0;
}
sinp = (struct sockaddr_in *) aip->ai_addr;
ip   = *(unsigned *) &sinp->sin_addr;
(我过去经常用三个SOCK_流、SOCK_DGRAM和SOCK_RAW返回3个addrinfo的列表,但提示阻止了这一点)

所以我的问题仍然存在…

看看这个:


请注意,在某些情况下,一台计算机可以有多个非环回IP地址,在这种情况下,该问题的答案会告诉您如何获取暴露于internet的IP地址。

不是答案,而是相关注释:请注意,一旦您开始发送数据包内容中的地址信息,您将面临使应用程序无法跨路由器和/或防火墙工作的风险

这些技术依赖于IP数据包报头中的信息来跟踪流量,如果应用程序在数据包内交换地址信息,而这些信息对该检查不可见,则它们可能会中断


当然,这可能与您的应用程序完全无关,但我认为在本文中值得指出。

即使计算机只有一个物理网络接口(这一假设可能成立,也可能不成立,即使上网本有两个以太网和WLAN),VPN也可以添加更多的IP地址。无论如何,另一端的主机应该能够确定您的主机用来联系它的IP。

使用

发送的数据包中将包含原始地址。。。没有必要重复此信息。它是在接受来自远程主机的通信时获得的(请参阅beej的《网络指南》,特别是上一部分)

有一个POSIX函数
getaddrinfo()
,它返回给定主机名的地址链接列表,因此您只需浏览该列表并找到非环回地址


请参见
mangetaddrinfo

您就快到了。我不确定您是如何从
hp
获得
my\u ip

gethostbyname()
返回指向具有
h\u addr\u列表
字段的
hostent
结构的指针

h\u addr\u list
字段是绑定到该主机的所有ip地址的以null结尾的列表

我认为您得到了环回地址,因为它是
h\u addr\u列表中的第一个条目

编辑:它应该是这样工作的:

gethostname(myhostname, 32);
hp = gethostbyname(myhostname);
unsigned my_ip = *(unsigned*)(hp->h_addr);

for (int i = 0; hp->h_addr_list[i] != 0; ++i) {
  if (hp->h_addr_list[i] != INADDR_LOOPBACK) {
    // hp->addr_list[i] is a non-loopback address
  }
}
// no address found

如果/etc/hosts仍然存在并且仍然不变,那么查找h_addr_列表中的所有条目将毫无帮助。

您的新代码将IPv4(在hint.ai_family字段中)的使用硬连接起来,这是一个糟糕的主意

除此之外,您已经很接近了,您只需要遍历getaddrinfo的结果。您的代码只获得第一个IP地址,但后面有一个aip->ai_下一个字段

struct addrinfo {
       ...
       struct addrinfo *ai_next;       /* next structure in linked list */
};

我遇到了这样一种情况:当只有/etc/hosts包含信息时,当我使用getaddrinfo获取IP地址列表时,每次都返回127.0.0.1。事实证明,主机名被化名为localhost…这一点通常很容易被忽略。发生的事情如下:

/etc/hosts文件:
127.0.0.1 localhost.localdomain localhost foo
::1 localhost6.localdomain6 localhost6
172.16.1.248 foo
172.16.1.249 bie
172.16.1.250气泡

现在,当您使用host=“foo”调用getaddrinfo时,它将返回127.0.0.1 3次。这里的错误是,foo同时出现在带有“127.0.0.1”和“172.16.1.248”的行中。一旦我用“127.0.0.1”将foo从行中删除,一切都很好


希望这对其他人有所帮助。

是的,这是无关紧要的,因为它总是在本地局域网上运行,但您的观点确实与更大规模的应用程序有关。请注意:代码的持久性超出了您的初衷。只是要知道,总有一天,有人会想在本地局域网之外运行它。(这可能没有任何意义,但他们会想这么做)。我需要从C中获得它,而不是从shell脚本或Java中。至少,公认的答案仍然是相关的-您可以从C访问该网站。gethostbyname多年来一直被弃用(原因之一是它只适用于一个地址族)。正如qrdl所提到的,您应该使用getaddrinfoOk,谢谢您提供的信息。此原始代码已有12年以上的历史。gethostbyname自1997年4月以来已被弃用。。。(RFC 2133的发布)我在原来的问题中添加了从hp now获得my_ip的行。您是什么意思,wat应该或不应该放在/etc/hosts中?问题是,在我们的普通系统(RHEL)上,主机名不是放在127.0.0.1条目之后(而是真正的eth0地址),但在像Ubuntu这样的系统上,他们将主机名放在127.0.0.1条目之后。如果您可以接受,请尝试删除(如果有疑问,请重命名)文件/etc/hosts,然后您将直接获得eth0 IP。可能是更糟糕的选择,但如果您只需要担心一台机器…在getaddrinfo结构后放置断点并浏览下一个ai_不会显示所有不同的ip地址,我只能获得本地地址或真实的以太网地址,这取决于/etc/hosts中主机名的位置。正如我在对问题的评论中所说的,无论如何,算法是有缺陷的。我的回答只是解释如何实现有缺陷的算法:-)是的,那很可能就是问题所在,我自己发现了