我想创建一个TCP服务器应用程序,它允许用户选择绑定调用中使用的本地地址。用户可能会提供主机名或IP地址的文本表示,因此我考虑使用getaddrinfo函数将文本表示转换为一个或多个sockaddr结构(必要时执行名称查找)。
现在我的问题是: getaddrinfo函数似乎不适合我的需要,因为它要求在提示结构中设置AI_PASSIVE标志,以便获得可以用于绑定调用的套接字地址。但是如果我使用AI_PASSIVE,就不能再使用nodename参数了,这就违背了让用户选择本地地址的整个目的。如果我不提供AI_PASSIVE,getaddrinfo将只返回那些可以用于connect、sendto和sendmsg调用的地址,但是可能有一些地址可以用于绑定,而不是用于连接、sendto或sendmsg调用,这些地址将被省略。请参阅关于getaddrinfo/freeaddrinfo函数的POSIX规范。
为了澄清我的需求,下面是我试图创建的应用程序的草图:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netdb.h>
int main(int argc, char **argv)
{
if (argc != 3) {
fprintf(stderr, "Usage: %s address port\n", argv[0]);
return EXIT_FAILURE;
}
struct addrinfo hints = {0};
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
struct addrinfo *result;
/* The next line won't work as intended! It will behave as if the
AI_PASSIVE flag was not set. */
if (getaddrinfo(argv[1], argv[2], &hints, &result) != 0) {
perror("getaddrinfo");
return EXIT_FAILURE;
}
/* Iterate result list */
int sfd;
struct addrinfo *rp = result;
do {
sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (sfd == -1)
continue;
if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0)
break; /* Success */
close(sfd);
rp = rp->ai_next;
} while (rp != NULL);
freeaddrinfo(result);
if (rp == NULL) { /* No address succeeded */
fprintf(stderr, "Could not bind\n");
return EXIT_FAILURE;
}
/* ... use socket bound to sfd ... */
close(sfd);
return EXIT_SUCCESS;
}实际上,我有几个关于这个话题的问题。首先,为什么在使用AI_PASSIVE标志时禁止nodename参数?目的是什么?对我来说没有任何意义。是否有任何方法(最好是POSIX-一致性)来找到我可以绑定的本地地址,以及与给定主机名或IP地址对应的本地地址?如果给定的节点名对应于本地地址,并且没有设置AI_PASSIVE,那么是否会有getaddrinfo返回的不能用于绑定的地址?或者更糟糕的是,是否有只适合绑定的地址,在这种情况下不会返回呢?
相关(未得到满意答复):如果指定了节点名,则被动忽略?
发布于 2016-08-18 22:28:48
我同意参考问题没有得到令人满意的答复,海事组织的整个特点没有得到明确的说明。
我相信AI_PASSIVE旗子的名字不对。它应该被称为类似AI_ANY_ADDRESS的东西。它的意思只是“给我一个令牌,我可以用来绑定到这个系统上的任何节点地址(地址家族中,等等)”,这样您就不必硬编码INADDR_ANY和IN6ADDR_ANY_INIT了。因此,如果您提供一个节点名称参数,那么您就删除了AI_PASSIVE的要点。你是说“这是我要绑定的地址”。
在实践中,这一切都很好,因为bind和connect所接受的地址结构在所有情况下都是基本相同的(除了不能对特殊的“任意”地址进行connect )。这就是为什么--当您指定节点名称时--不管您是否指定AI_PASSIVE。您将获得针对指定名称/地址的bind或connect调用的适当参数。
获取“地址信息”并不意味着bind或connect会成功。您可以成功地获得一个您不能bind到的地址--因为它不是本地计算机的地址--或者也因为其他原因。(很明显,connect也可能因为许多原因而失败-在该地址上可能没有应答机器,或者没有服务器监听端口等等)。
如果您提供一个非IP地址的节点名称,并且名称解析提供不同家族中的多个IP地址(这些地址在本地计算机上都可用),则应该能够对所有这些地址进行bind。
因此,底线是:如果您希望允许用户指定地址,只需将其提供给getaddrinfo即可。返回的地址(如果有的话)应该可以在bind调用中使用。
https://stackoverflow.com/questions/39026202
复制相似问题