ai_protocol在指定ai_socktype时调用getaddrinfo()时可以在提示中提供什么其他用途?

时间:2016-09-13 16:33:13

标签: c linux sockets

getaddrinfo接受struct addrinfo *hints作为第三个参数,可用于指定选择此函数返回的套接字地址的条件。

文档说我们可以设置ai_socktype以及ai_protocol来指定我们的选择标准。但是,如果我们已经指定ai_protocol,我无法理解为什么需要ai_socktype。如果指定了这两个中的一个,那么另一个似乎是多余的。

以下是我为实验而编写的一些代码。

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>

void getaddrinfo_demo(const char *node, const char *service,
                      int socktype, int protocol)
{
    struct addrinfo hints, *res, *p; 
    int error;

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_INET;
    hints.ai_socktype = socktype;
    hints.ai_protocol = protocol;

    error = getaddrinfo(node, service, &hints, &res);
    if (error) {
        printf("Error %d: %s\n\n", error, gai_strerror(error));
        return;
    }

    for (p = res; p != NULL; p = p->ai_next) {
        struct sockaddr_in *addr = ((struct sockaddr_in *) p->ai_addr);
        char ip[INET_ADDRSTRLEN];
        int port = ntohs(addr->sin_port);

        inet_ntop(AF_INET, &addr->sin_addr, ip, INET_ADDRSTRLEN);
        printf("ip: %s; port: %d; protocol: %d\n", ip, port, p->ai_protocol);
    }
    printf("\n");

    freeaddrinfo(res);
}

int main()
{
    /* Consistent family and socktype works fine. */
    getaddrinfo_demo("localhost", "http", SOCK_STREAM, IPPROTO_TCP);
    getaddrinfo_demo("localhost", "http", SOCK_DGRAM, IPPROTO_UDP);

    /* Inconsistent family and sock type leads to error -7. */
    getaddrinfo_demo("localhost", "http", SOCK_STREAM, IPPROTO_UDP);
    getaddrinfo_demo("localhost", "http", SOCK_DGRAM, IPPROTO_TCP);
}

这是输出。

$ gcc -std=c99 -D_POSIX_SOURCE -Wall -Wextra -pedantic foo.c && ./a.out 
ip: 127.0.0.1; port: 80; protocol: 6
ip: 127.0.0.1; port: 80; protocol: 6

ip: 127.0.0.1; port: 80; protocol: 17
ip: 127.0.0.1; port: 80; protocol: 17

Error -7: ai_socktype not supported

Error -7: ai_socktype not supported

正如您可以看到ai_socktype = AF_STREAM,只有ai_protocol = IPPROTO_TCP有效。指定ai_protocol = IPPROTO_UDP会导致错误。如果我们无法通过它指定任何其他选择标准,也可以省略在ai_protocol中指定hints

那么ai_protocolhints的作用究竟是什么?您能举例说明ai_socktypeai_protocol是否有用途?

1 个答案:

答案 0 :(得分:9)

这些怎么样:

getaddrinfo_demo("localhost", "http", SOCK_STREAM, IPPROTO_SCTP);
getaddrinfo_demo("localhost", "imap", SOCK_STREAM, IPPROTO_TCP);
getaddrinfo_demo("localhost", "imap", SOCK_STREAM, IPPROTO_SCTP);
getaddrinfo_demo("localhost", "sbcap", SOCK_STREAM, IPPROTO_SCTP);
getaddrinfo_demo("localhost", "sbcap", SOCK_SEQPACKET, IPPROTO_SCTP);
getaddrinfo_demo("localhost", "sbcap", SOCK_STREAM, IPPROTO_TCP);
getaddrinfo_demo("localhost", "http", SOCK_DGRAM, IPPROTO_UDPLITE);
getaddrinfo_demo("localhost", "syslog-tls", SOCK_DCCP, IPPROTO_DCCP);

哪个给你:

ip: 127.0.0.1; port: 80; protocol: 132
ip: 127.0.0.1; port: 143; protocol: 6
Error -8: Servname not supported for ai_socktype
ip: 127.0.0.1; port: 29168; protocol: 132
ip: 127.0.0.1; port: 29168; protocol: 132
Error -8: Servname not supported for ai_socktype
Error -8: Servname not supported for ai_socktype
ip: 127.0.0.1; port: 6514; protocol: 33

因此,TCP不是唯一的流协议,并且UDP不是唯一的数据报协议(尽管DCCP有自己的SOCK_DCCP且UDP-Lite在服务数据库中没有任何条目(可能是认为它不需要UDP-Lite RFC明确表示“UDP-Lite使用由IANA分配的同一组端口号值供UDP使用”)。)

我们现在在实践中并没有经常看到它,但如果我们谈论像getaddrinfo()这样的API,它们必须设计得有点面向未来,这包括制作一些看似多余的东西。只有时间可以判断这些事情是否真的是多余的。在这种情况下,它不是,它恰恰相反。如果FreeBSD man page是正确的,那么

  

该实现首次出现在WIDE Hydrangea IPv6协议栈工具包中。

互联网上仍然存在关于此协议栈的FAQ,我们可以猜测它是在1997 - 1998年左右创建的(我不是那么老,要记住这些东西而且我不是看到任何其他合适的来源,如果我错了,请纠正我)。并且在2000年SCTP was defined。正如我在上面的示例中所示,我们使用此API的SCTP没有任何问题。与DCCP相同的故事出现在2005-2006左右,完全符合相同的API。