【问题标题】:getting local host name as destination using getaddrinfo/getnameinfo使用 getaddrinfo/getnameinfo 获取本地主机名作为目标
【发布时间】:2026-02-22 02:00:01
【问题描述】:

我正在查看一些过时的代码,这些代码使用 getaddrinfogetnameinfo 来确定主机名信息,然后在 getnameinfo 失败时回退到 gethostnamegethostbyname

现在,这对我来说似乎是错误的。我正在尝试了解代码的意图,以便提出建议。我不想在这里重新发布整个代码,因为它又长又复杂,但我会尝试总结一下:

据我所知,这段代码的重点是生成一个字符串,另一个进程可以使用该字符串连接到侦听套接字。这似乎不仅适用于本地进程,也适用于远程主机连接回这台计算机。

所以有问题的代码基本上是在做以下事情:

  • getaddrinfo(node = NULL, service = port, hints.ai_flags = AI_PASSIVE, ai); -- 这将获取socket() 的可能参数列表,这些参数可以与bind() 一起使用。
  • 浏览结果列表并创建一个套接字。
  • 第一次成功创建套接字时,将其选为“已使用”addrinfo
  • 对于选中的addrinfo中的ai_addr,调用getnameinfo()获取关联的主机名。
  • 如果失败,请致电gethostname(),然后在结果中查找gethostbyname()

我认为这是错误的有几个原因,但我想验证我的逻辑。首先,从一些实验看来,getnameinfo() 在这里几乎总是失败。我想输入地址是未知的,因为它是一个监听套接字,而不是一个目的地,所以从这个角度来看,它不需要一个有效的 IP。然后,调用gethostname() 并将结果传递给gethostbyname() 几乎总是返回与gethostname() 本身相同的结果。换句话说,它只是验证本地主机名,对我来说似乎毫无意义。这是有问题的,因为它甚至不一定能被远程主机使用,不是吗?

不知何故,我认为尝试在子网上确定您自己的主机名的整个想法可能不是那么有用,而是您必须向另一台主机ping 一条消息并查看他们认为它是什么IP 地址。 (不幸的是,在这种情况下这没有意义,因为我不知道该程序级别的其他对等方。)例如,本地主机可能有多个 NIC,因此有多个 IP 地址,因此尝试确定单个主机地址对是无意义的。 (仅bind() 并同时收听所有 addrinfo 结果是否正确?)

我还注意到,只需将名称传递给getaddrinfo() 并设置AI_CANONNAME 标志即可解析名称,这意味着getnameinfo() 步骤可能是多余的。但是,我想这里没有这样做,因为他们试图在不提供先验信息的情况下确定主机名的某种公正视图。当然,它失败了,他们最终还是使用了gethostname()!我还尝试向getaddrinfo() 提供“localhost”,它会在 ai_canonname` 中报告 Linux 下的主机名,但在 OS X 上只会导致“localhost”,所以它不是那么有用,因为这应该是跨平台的。

我想总结一下,我的问题是,在现代套接字编程中,正确方法是什么?我倾向于用简单地返回 gethostname() 的结果来替换这段代码,但我想知道是否有更合适的解决方案使用像 getaddrinfo() 这样的现代调用。

如果答案是没有办法做到这一点,我只能使用gethostname(),因为我必须在这里返回一些东西,否则会破坏 API。

【问题讨论】:

    标签: sockets


    【解决方案1】:

    如果我没看错,您只是想获得一个非本地套接字地址,该地址可能会成功创建本地套接字,并让远程主机重新连接。

    我写了一个函数,你可以引用它,叫做“GetBestAddressForSocketBind”。您可以从我的 GitHub 项目页面 here 获取它。您可能需要引用父目录中的一些代码。

    代码本质上只是使用getifaddrs 来枚举适配器并选择第一个“启动”的适配器,而不是环回/本地并且具有所需地址系列(AF_INET 或 AF_INET6)的 IP 地址。

    希望这会有所帮助。

    【讨论】:

    • 这类似于我在另一个项目中采用的方法,但最终我意识到在计算机是可能同时具有有线和无线连接的笔记本电脑的情况下,这是有问题的。在选择“第一个”报告的 IP 时,通常发生的情况是选择了错误的 IP,并且无法作为远程主机的目标。它似乎也与一些在某些机器上安装“假”网卡的复制保护软件发生了严重冲突。最好只向远程发送一条 UDP 消息,并让它使用 recvfrom() 检查 UDP 数据报的来源。
    • 这是 Windows 还是 Linux?在任何情况下,这两个平台都允许您检查路由表以发现哪个 IP/适配器具有默认路由。对于多个适配器方案,具有最低指标的那个可能是默认值。但是唯一确定的方法就是绑定到 INADDR_ANY,然后让对方告诉你它认为你是什么 IP。嘿,这听起来像 STUN。我的答案中与上面的链接相同。
    【解决方案2】:

    我认为你应该看看Ulrich Drepper's 关于 IPv6 编程的文章。它相对较短,可能会回答您的一些问题。我发现它真的很有用。我发布此链接,因为没有(至少)伪代码很难回答您的问题。

    【讨论】:

    • 它似乎没有解决如何将主机名与对等方相关联的问题。然而,提到getifaddrs 可能会导致扫描本地IP 列表并在每个IP 上调用getnameinfo 以获取可能名称的列表。我试试看。