如何使用getaddrinfo选择服务器套接字地址? [英] How to choose a server socket address using getaddrinfo?

查看:267
本文介绍了如何使用getaddrinfo选择服务器套接字地址?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I would like to create a TCP server application which lets the user choose the local address that is used in the bind call. The user may provide a textual representation of a host name or IP address, so I thought of using the getaddrinfo function to translate the textual representation into one or several sockaddr structs (performing name lookup if necessary).

现在这是我的问题:getaddrinfo函数似乎不符合我的需求,因为它需要在hints结构中设置AI_PASSIVE标志,以便获得可在绑定调用中使用的套接字地址.但是,如果我使用AI_PASSIVE,则不能再使用nodename参数,这使让用户选择本地地址的整个目的失去了意义.如果我不提供AI_PASSIVE,则getaddrinfo将仅返回可用于connect,sendto和sendmsg调用的地址,但可能会有一些地址可用于绑定,但不能用于connect,sendto或sendmsg调用,将省略这些地址. .有关 getaddrinfo/freeaddrinfo 函数的信息,请参阅POSIX规范.

Now here's my problem: The getaddrinfo function does not seem to suit my needs, because it requires the AI_PASSIVE flag to be set in the hints structure in order to obtain socket addresses that may be used in bind calls. But if I use AI_PASSIVE, I can not use the nodename parameter anymore, which defeats the whole purpose of letting the user choose the local address. If I do not provide AI_PASSIVE, getaddrinfo will only return those addresses which can be used in connect, sendto and sendmsg calls, but there might be addresses that can be used for binding but not for connect, sendto or sendmsg calls, which would be omitted. See the POSIX specification regarding the getaddrinfo/freeaddrinfo functions.

为阐明我的需求,以下是我尝试创建的应用程序的草图:

To clarify my needs, here is a sketch of the application I am trying to create:

#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是否会返回任何不能用于绑定的地址?或更糟糕的是,会有任何仅适用于绑定并且在这种情况下不会返回的地址吗?

I actually have several questions regarding the topic. First off, why is the nodename parameter forbidden when using the AI_PASSIVE flag? What is the intention? It does not make any sense to me. Is there any (preferably POSIX-conform) way to find local addresses that I can bind to and that correspond to a given host name or IP address in textual representation at all? Provided that the given nodename corresponds to a local address and AI_PASSIVE is not set, will there be any addresses returned by getaddrinfo, that can not be used for binding? Or worse, will there be any addresses that are only suitable for binding and that will not be returned in this case?

相关(且未得到令人满意的答复):

Related (and not answered satisfactorily): getaddrinfo: in what way is AI_PASSIVE ignored if the nodename is specified?

推荐答案

我同意无法令人满意地回答参考问题,并且IMO的整个功能指定不当.

I agree the reference question was not answered satisfactorily and IMO the whole feature is poorly specified.

我相信AI_PASSIVE标志的名称错误.它应该被称为AI_ANY_ADDRESS之类的东西.这只是意味着给我一个令牌,我可以使用它来绑定到该系统上的任何节点地址(在地址族内,等等)",这样您就不必对INADDR_ANYIN6ADDR_ANY_INIT进行硬编码.因此,如果提供节点名称参数,则可以避免AI_PASSIVE的问题.您说的是这是我要绑定的地址".

I believe the AI_PASSIVE flag is misnamed. It should be called something like AI_ANY_ADDRESS. It just means "give me a token I can use to bind to any node address on this system (within the address family, etc)" so that you don't have to hard-code INADDR_ANY and IN6ADDR_ANY_INIT. Hence, if you supply a node name argument, you're obviating the point of AI_PASSIVE. You're saying "Here's the address(es) I want to bind to".

实际上,这一切都很好,因为bindconnect接受的地址结构在所有情况下基本相同(除了您不能connect到特殊的"any"地址) .这就是为什么-当您指定节点名称时-是否指定AI_PASSIVE都无关紧要.您将在指定的名称/地址上获取适合bindconnect调用的参数.

In practice, this all works out fine because the structure of addresses accepted by bind and connect is fundamentally the same in all cases (except that you can't connect to the special "any" address). That's why -- when you specify the node name -- it doesn't matter whether you specify AI_PASSIVE or not. You're going to get the parameters appropriate for a bind or connect call on the specified name/address.

获得地址信息"并不意味着bindconnect将成功.您可以成功获取一个不能bind的地址-因为它不是本地计算机的地址-或由于其他原因. (显然,connect也可能由于多种原因而失败-可能没有机器在该地址处应答,或者没有服务器在端口上侦听,等等.)

Obtaining that "address info" doesn't mean the bind or connect will succeed. You could successfully obtain an address that you can't bind to -- because it's not an address of the local machine -- or for other reasons as well. (And obviously, the connect could fail for many reasons as well -- there might be no machine answering at that address or no server listening on the port, etc.)

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

If you supply a node name that is not an IP address and name resolution provides multiple IP addresses in different families (that are all available on the local machine), you should be able to bind to all of those addresses.

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

So, bottom line: If you want to allow the user to specify the address, just provide that to getaddrinfo. The address(es) you get back (if any) should be usable in a bind call.

这篇关于如何使用getaddrinfo选择服务器套接字地址?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆