3

对于两台主机之间的通信,我需要将我的主机的 IP 地址发送到另一个站点。问题是,如果我请求我的 IP 地址,可能是我取回了本地环回 IP 地址 (127.xxx) ,而不是网络(以太网)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_STREAM、SOCK_DGRAM 和 SOCK_RAW 取回 3 个 addrinfo 的列表,但提示阻止了这一点)

所以我的问题仍然存在......

4

10 回答 10

9

有 POSIX 函数getaddrinfo()可以返回给定主机名的地址链接列表,因此您只需要遍历该列表并找到非环回地址。

man getaddrinfo

于 2009-03-09T10:36:30.837 回答
4

不是一个答案,而是一个相关的评论:请注意,一旦您开始在数据包的内容中发送地址信息,您就会冒着使您的应用程序无法跨NAT:ing路由器和/或防火墙工作的风险。

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

当然,这可能与您的应用程序完全无关,但我认为在这种情况下值得指出。

于 2009-03-09T10:22:25.003 回答
4

原始地址将包含在发送的数据包中……无需复制此信息。它是在接受来自远程主机的通信时获得的(请参阅 beej 的网络指南,特别是有关accept()的部分)

于 2009-03-09T10:27:02.730 回答
3

我刚刚遇到了这样一种情况,当只有 /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 bletch

因此,现在,当您使用 host="foo" 调用 getaddrinfo 时,它会返回 127.0.0.1 3 次。这里的错误是 foo 出现在“127.0.0.1”和“172.16.1.248”的行上。一旦我从带有“127.0.0.1”的行中删除了 foo ,一切正常。

希望这可以帮助某人。

于 2010-01-08T22:08:08.217 回答
1

看看这个:以 编程方式发现公共 IP

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

于 2009-03-09T10:19:04.577 回答
0

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

于 2009-03-09T10:22:57.530 回答
0

采用getaddrinfo()

于 2009-03-09T10:27:01.750 回答
0

您快到了。我不确定你是如何my_iphp.

gethostbyname()返回指向hostent具有h_addr_list字段的结构的指针。

h_addr_list字段是绑定到该主机的所有 IP 地址的以空字符结尾的列表。

我认为您获得了环回地址,因为它是h_addr_list.

编辑:它应该像这样工作:

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
于 2009-03-09T14:04:56.030 回答
0

如果 /etc/hosts 仍然存在并且仍然相同,则查找 h_addr_list 的所有条目将无济于事。

于 2009-03-31T15:01:13.137 回答
0

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

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

struct addrinfo {
       ...
       struct addrinfo *ai_next;       /* next structure in linked list */
};
于 2009-03-31T21:37:33.717 回答