如果 getaddrinfo 失败一次,它将永远失败(即使在网络准备就绪之后)

If getaddrinfo fails once, it fails forever (even after network is ready)

我正在编写一个 C 应用程序,它在启动时 运行 作为 systemd service(发行版:Arch Linux)并且应该连接到服务器。因为应用程序在启动时 运行 最终会发生网络连接尚未建立的情况。这自然会导致需要一个函数的第一个函数失败,在我的例子中是 getaddrinfo.

所以我认为我只需要编写一个循环,重复调用 getaddrinfo 直到网络准备就绪后它成功。不幸的是,我发现即使在建立连接后 getaddrinfo 仍然失败 name or service not known

我可以通过主机名 ping 服务器,但 getaddrinfo 仍然无法执行此操作。如果我停止应用程序并再次 运行 它,一切正常。如果在第一次调用之前已经建立网络连接,getaddrinfo 也可以正常工作。

显然,如果getaddrinfo因为网络未就绪而失败一次,它将永远失败。它似乎没有意识到现在存在的连接。使用已弃用的 gethostbyname 时,行为相同。

这种行为的原因是什么?有没有办法强制 getaddrinfo 刷新内部变量(如果存在)或类似的东西,这可以解释为什么函数仍然认为没有连接?为了检查网络是否准备就绪,我应该提前调用另一个函数吗?

我想避免等待一段时间的延迟,期待网络在之后连接。我也更愿意从我的应用程序中检查连接,而不是让 bash 脚本先检查它然后启动应用程序。

您可以通过编译以下测试程序并按照以下说明理解答案:

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    while (1)
    {
        struct addrinfo *res;
        int rc=getaddrinfo(argv[1], "http", NULL, &res);

        printf("getaddrinfo returned %d\n", rc);

        if (rc == 0)
            freeaddrinfo(res);

        sleep(1);
    }
}

在你运行这个测试程序之前:

  1. 连接到网络。
  2. 重命名,暂时/etc/resolv.conf改为/etc/resolv.conf.save
  3. 启动这个测试程序,使用一个好的主机名。
  4. 测试程序启动并开始打印错误代码后不久,将 /etc/resolv.conf.save 重命名为 /etc/resolv.conf
  5. 观察测试程序仍然报DNS解析失败
  6. 不过,如果您按 CTRL-C 并重新启动它,测试程序现在将报告有效的 DNS 解析。

当您断开网络连接并重新连接时,您的网络堆栈会相应地重写和更新 /etc/resolv.conf。 C 库中的 DNS 解析器需要此配置文件。 C库第一次从/etc/resolv.conf读取DNS配置,并缓存。它不会在每次查找时检查 /etc/resolv.conf 的内容是否已更改。

最后:

  1. 你的家庭作业是在这个测试程序中添加对 res_init() 的调用,定义在 resolv.h 中,阅读相应的手册页,看看会发生什么。这就是你的答案。