在 C 中使用 bind() 时获取错误的 IP 地址

Posted

技术标签:

【中文标题】在 C 中使用 bind() 时获取错误的 IP 地址【英文标题】:Get wrong IP address when using bind() in C 【发布时间】:2012-01-04 14:55:31 【问题描述】:

我在 C 中使用套接字编程 API。在 TCP 客户端程序中,我使用 bind(),然后使用 getsockname()(在调用 connect() 之前)获取本地机器的 IP 地址和端口号.但是,我只能得到正确的端口号,而得到错误的 IP 地址(零)。

所以我想问是否有任何方法可以正确获取本地机器的正确 IP 地址(在调用 connect() 之前)?

【问题讨论】:

你能展示一下你是如何绑定套接字的吗? 如果您告诉我们为什么需要本地 IP 地址会很有帮助。 这一切都取决于你如何绑定()套接字。显示该代码。如果你使用 INADDR_ANY,当你在 connect() 之前调用 getsockname() 时,你会得到它。如果您在 connect() 之后调用 getsockname(),您将获得用于通信的本地 IP。 @Juliano 这似乎不太相关,学习本地地址有很多用途(虽然,在建立通信之后) @nos 关于调用getsockname()连接之后的好点。 @nos 真的,有什么用? (想想 NAT,以及它如何破坏您认为本地 IP 地址可能有任何用途的东西)。 【参考方案1】:

没有一种简单或可移植的方式来做你想做的事。在您的情况下,bind()getsockname() 的行为是正确的。

您的程序不应以任何方式依赖或要求主机的 IP 地址。如果是这样,您的程序中可能存在一些设计问题。

问问自己,一台具有两个网络接口、连接到两个不同网络和多个 IP 地址的计算机的 IP 地址是什么?

     Network 1                  Network 2
...-----------+--            --+-----------...
               \              /
     192.0.2.1  \            /  198.51.100.2
          eth0  +------------+  eth1
                |  Computer  |
                +------------+

问:您希望在这种情况下看到什么 IP 地址?

答案是没有正确答案,您的程序甚至不应该为此烦恼。您的程序应该更关心建立通信,为此您只需要目标 IP 或主机名。操作系统会找到一种建立通信的方法(对于面向连接的流,它可能能够为您提供选择与该目标通信的本地地址)。

您可以使用gethostname() 检索当前主机名,并使用该名称尝试查找 IP 地址,但仍有很多陷阱:

    不要求或保证主机名具有任何关联的 IP 地址。 在很多情况下,与主机名关联的 IP 地址是 127.0.0.1,这没有用(大多数 Linux 发行版都将它添加到 /etc/hosts 文件中)。 即使您从主机名中获得 IP 地址,它也不太可能有任何用处(如上面的情况 2,或 NAT 后面的本地 IP 地址,或错误网络接口的 IP)。

因此,您一开始就不应该依赖这些信息。

【讨论】:

【参考方案2】:

一种可移植的方法是首先调用 gethostname(),然后调用 gethostbyname()。

即。

char hostname[256], ipaddress[256];
if (gethostname(hostname, sizeof(hostname)) != SOCKET_ERROR) 
    struct hostent *phe = gethostbyname(hostname);
    if (phe != NULL && phe->h_addr_list[0] != NULL) 
        struct in_addr addr;
        memcpy(&addr, phe->h_addr_list[0], sizeof(struct in_addr));
        strcpy(ipaddress, inet_ntoa(addr));
    

【讨论】:

一个以break 语句结尾的for 构造,并且在任何地方都没有continue 如果您的本地主机文件或 DNS 不一致,这将失败。首选方法是使用依赖于操作系统的方法来枚举各个网络接口。 @Alnitak,是的,同意。 flybin 问了一个简单的问题,我提供了一个似乎符合他当前需求的答案。【参考方案3】:

您可以使用gethostname 获取主机名,然后使用gethostbyname 获取IP 地址。

【讨论】:

虽然不是 100% 可靠。 gethostbyname() 会进行 DNS 查找,因此如果 hostname 具有与之关联的额外 DNS 条目(负载平衡等),它最终可能会返回您不期望的内容。【参考方案4】:

如果您绑定到INADDR_ANY,那么这就是您从getsockname() 获得的地址。

特别是,在具有多个接口的机器上,您的套接字使用的地址将取决于路由表和远程客户端的地址。

例如,如果您在环回接口上连接到自己,您将使用127.0.0.1,而如果您连接到外部的某个人,那么它将是您的以太网接口的(一个)地址。

正确的方法当且仅当您确实需要绑定已知地址时是枚举您机器的网络接口(如果您使用的是 Linux,请参阅man netdevice),然后提供特定的想要的地址到bind()

【讨论】:

【参考方案5】:

您可以使用gethostip()获取主机IP地址。

【讨论】:

有一个特定于 Linux 的命令 gethostip,但在任何地方都没有具有该名称的 C 函数。也许你的意思是gethostname()

以上是关于在 C 中使用 bind() 时获取错误的 IP 地址的主要内容,如果未能解决你的问题,请参考以下文章

使用boost bind时无效使用非静态成员函数 - c ++

无法通过ip访问redis服务

DNS and BIND

这是对 std::bind 的错误使用还是编译器错误?

13 使用Bind提供域名解析服务

我可以在同一个套接字描述符上调用 bind() 然后 connect() 吗?