如果 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);
}
}
在你运行这个测试程序之前:
- 连接到网络。
- 重命名,暂时
/etc/resolv.conf
改为/etc/resolv.conf.save
。
- 启动这个测试程序,使用一个好的主机名。
- 测试程序启动并开始打印错误代码后不久,将
/etc/resolv.conf.save
重命名为 /etc/resolv.conf
。
- 观察测试程序仍然报DNS解析失败
- 不过,如果您按 CTRL-C 并重新启动它,测试程序现在将报告有效的 DNS 解析。
当您断开网络连接并重新连接时,您的网络堆栈会相应地重写和更新 /etc/resolv.conf
。 C 库中的 DNS 解析器需要此配置文件。 C库第一次从/etc/resolv.conf
读取DNS配置,并缓存。它不会在每次查找时检查 /etc/resolv.conf
的内容是否已更改。
最后:
- 你的家庭作业是在这个测试程序中添加对
res_init()
的调用,定义在 resolv.h
中,阅读相应的手册页,看看会发生什么。这就是你的答案。
我正在编写一个 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);
}
}
在你运行这个测试程序之前:
- 连接到网络。
- 重命名,暂时
/etc/resolv.conf
改为/etc/resolv.conf.save
。 - 启动这个测试程序,使用一个好的主机名。
- 测试程序启动并开始打印错误代码后不久,将
/etc/resolv.conf.save
重命名为/etc/resolv.conf
。 - 观察测试程序仍然报DNS解析失败
- 不过,如果您按 CTRL-C 并重新启动它,测试程序现在将报告有效的 DNS 解析。
当您断开网络连接并重新连接时,您的网络堆栈会相应地重写和更新 /etc/resolv.conf
。 C 库中的 DNS 解析器需要此配置文件。 C库第一次从/etc/resolv.conf
读取DNS配置,并缓存。它不会在每次查找时检查 /etc/resolv.conf
的内容是否已更改。
最后:
- 你的家庭作业是在这个测试程序中添加对
res_init()
的调用,定义在resolv.h
中,阅读相应的手册页,看看会发生什么。这就是你的答案。