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_protocol
中hints
的作用究竟是什么?您能举例说明ai_socktype
和ai_protocol
是否有用途?
答案 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。