getaddrinfo接受struct addrinfo *hints
作为第三个参数,可以用来指定select此函数返回的套接字地址的条件。
文档说,我们可以设置ai_socktype
以及ai_protocol
来指定我们的select标准。 但是,我不明白为什么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
导致错误。 如果我们不能通过它指定任何额外的select标准,那么可以省略在hints
指定ai_protocol
。
那么hints
中ai_protocol
的作用是什么呢? 你能举一个例子,其中ai_socktype
和ai_protocol
都有一定的作用吗?
这些怎么样:
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手册页是正确的,那么
该实现首先出现在WIDE Hydrangea IPv6协议栈套件中。
在互联网上还有一个关于这个协议栈的常见问题 ,从中我们可以猜测它是在1997年 – 1998年左右创建的(我不是那么古老,不记得这些东西,我没有看到任何其他适当的来源,所以纠正我,如果我错了)。 而SCTP是在2000 年定义的。正如我上面的例子所显示的,我们使用这个API的SCTP没有任何问题。 与2005年 – 2006年左右出现的DCCP一样,完全适用于相同的API。