【问题标题】:How to choose a server socket address using getaddrinfo?如何使用 getaddrinfo 选择服务器套接字地址?
【发布时间】:2016-08-18 19:34:19
【问题描述】:

我想创建一个 TCP 服务器应用程序,让用户选择绑定调用中使用的本地地址。用户可能会提供主机名或 IP 地址的文本表示,因此我想到使用 getaddrinfo 函数将文本表示转换为一个或多个 sockaddr 结构(必要时执行名称查找)。

现在我的问题是:getaddrinfo 函数似乎不适合我的需要,因为它需要在提示结构中设置 AI_PASSIVE 标志才能获得可能在绑定调用中使用的套接字地址。但是如果我使用 AI_PASSIVE,我就不能再使用 nodename 参数,这违背了让用户选择本地地址的整个目的。如果我不提供 AI_PASSIVE,getaddrinfo 将只返回那些可用于 connect、sendto 和 sendmsg 调用的地址,但可能有可用于绑定但不能用于 connect、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 not 设置,getaddrinfo 返回的地址是否不能用于绑定?或者更糟糕的是,会不会有什么地址只适合绑定,在这种情况下不会返回?

相关(但未得到满意答复):getaddrinfo: in what way is AI_PASSIVE ignored if the nodename is specified?

【问题讨论】:

    标签: sockets unix network-programming posix getaddrinfo


    【解决方案1】:

    我同意参考问题没有得到令人满意的回答,而且 IMO 的整个功能都没有得到很好的说明。

    我认为 AI_PASSIVE 标志的名称有误。它应该被称为AI_ANY_ADDRESS。它只是意味着“给我一个令牌,我可以用它来绑定到这个系统上的任何节点地址(在地址族等内)”,这样你就不必硬编码INADDR_ANYIN6ADDR_ANY_INIT。因此,如果您提供节点名称参数,您将忽略AI_PASSIVE 的点。你是说“这是我要绑定的地址”。

    实际上,这一切都很好,因为bindconnect 接受的地址结构在所有情况下都基本相同(除了你不能connect 到特殊的“任何”地址) .这就是为什么 - 当您指定节点名称时 - 您是否指定 AI_PASSIVE 并不重要。您将获得适用于指定名称/地址的bindconnect 调用的参数。

    获得“地址信息”并不意味着bindconnect 会成功。您可以成功获得一个您无法bind 的地址——因为它不是本地机器的地址——或者也有其他原因。 (显然,connect 也可能由于多种原因而失败——可能没有机器在该地址应答或没有服务器在端口上监听,等等)

    如果您提供的节点名称不是 IP 地址,并且名称解析提供了不同系列的多个 IP 地址(在本地计算机上都可用),您应该能够 bind 到所有这些地址。

    因此,底线:如果您想允许用户指定地址,只需将其提供给getaddrinfo。您返回的地址(如果有)应该可以在bind 调用中使用。

    【讨论】:

    • 因此,在绑定/连接地址之间进行区分的规范只是明显的误导,没有任何意义。如果没有这种区别,作为 INADDR_ANY/IN6ADDR_ANY_INIT 的简单抽象的 AI_PASSIVE 标志本身仍然有用,并且所有用例都得到满足。感谢您的精彩解释!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-01
    • 2011-09-27
    • 1970-01-01
    • 2015-07-25
    • 1970-01-01
    相关资源
    最近更新 更多