2016-08-18 25 views
0

我想创建一个TCP服务器应用程序,让用户选择在绑定调用中使用的本地地址。用户可以提供主机名或IP地址的文本表示,所以我想使用getaddrinfo函数将文本表示转换为一个或多个sockaddr结构(如果需要,执行名称查找)。如何使用getaddrinfo选择服务器套接字地址?

现在这是我的问题: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将返回哪些地址,这些地址不能用于绑定?或者更糟糕的是,是否有任何地址只适合绑定,在这种情况下不会返回?

相关的(而不是回答令人满意):getaddrinfo: in what way is AI_PASSIVE ignored if the nodename is specified?

回答

3

我同意引用的问题没有得到满意的答复和国际海事组织全功能指定很差。我相信AI_PASSIVE标志是错误的。它应该被称为AI_ANY_ADDRESS。它只是意味着“给我一个令牌,我可以使用它绑定到此系统上的任何节点地址(在地址族中)”,以便您不必硬编码INADDR_ANYIN6ADDR_ANY_INIT。因此,如果您提供了一个节点名称参数,则可以避免使用AI_PASSIVE。你说“这是我想要绑定的地址”。

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

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

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

所以,底线:如果你想允许用户指定地址,只需提供到getaddrinfo。你回来的地址(如果有的话)应该可以用于拨打bind

+0

因此,这种区分地址之间的绑定/连接的规范只是简单的误导,没有任何意义。没有这种区别,AI_PASSIVE标志作为INADDR_ANY/IN6ADDR_ANY_INIT的简单抽象仍然是有用的,并且所有用例都满足。谢谢你这个伟大的解释! – purefanatic

相关问题