有人可以在下面的代码中向我解释一下为什么吗:
getaddrinfo("localhost", "600", &hints, &listp)
创建两个套接字地址,分别是 0.0.0.0
和 127.0.0.1
?
输出:
IP address: 0.0.0.0
sin_family: 30
sin_port: 600
IP address: 127.0.0.1
sin_family: 2
sin_port: 600
listenfd: -1
getaddrinfo("localhost", "6060", &hints, &listp)
创建一个套接字地址0.0.0.0
?
输出:
IP address: 0.0.0.0
sin_family: 30
sin_port: 6060
listenfd: 5
getaddrinfo("127.0.0.1", "6060", &hints, &listp)
创建一个套接字地址127.0.0.1
,绑定到listenfd
3?
输出:
IP address: 127.0.0.1
sin_family: 2
sin_port: 6060
listenfd: 3
getaddrinfo(NULL, "6060", &hints, &listp)
创建一个套接字地址0.0.0.0
,绑定到listenfd
4?
输出:
IP address: 0.0.0.0
sin_family: 30
sin_port: 6060
listenfd: 4
#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/un.h>
#define LISTENQ 10
int open_listenfd()
{
struct addrinfo hints, *listp, *p;
int s, listenfd, optval=1;
// Get a list of potential server addresses
memset(&hints, 0, sizeof(struct addrinfo));
// hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
hints.ai_flags |= AI_NUMERICSERV;
s = getaddrinfo(NULL, "600", &hints, &listp);
if (s != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
exit(EXIT_FAILURE);
}
// Walk the list for one that we can bind to
for(p = listp; p; p = p->ai_next) {
struct sockaddr_in *addr_in = (struct sockaddr_in *)p->ai_addr;
char *m = inet_ntoa(addr_in->sin_addr);
printf("IP address: %s\n", m);
printf("sin_family: %i\n", addr_in->sin_family);
printf("sin_port: %i\n", htons(addr_in->sin_port));
// Create a socket descriptor
if ((listenfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol))<0)
continue;
// Eliminates "Address already in use"l error from bind
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval, sizeof(int));
// Bind the descriptor to the address
if(bind(listenfd, p->ai_addr, p->ai_addrlen) == 0)
break;
close(listenfd); // Bind failed, try the next
}
// Clean up
freeaddrinfo(listp);
if(!p) // No address worked
return -1;
// Make it a listening socket ready to accept connectionn request
if(listen(listenfd, LISTENQ) < 0) {
close(listenfd);
return -1;
}
return listenfd;
}
int main() {
int listenfd;
listenfd = open_listenfd();
printf("listenfd: %i\n", listenfd);
}
难道
getaddrinfo("localhost", "600", &hints, &listp)
不应该创建一个具有只有一个IP地址127.0.0.0
的套接字地址吗,即使端口600由于某种原因无法使用?
既然
getaddrinfo("localhost", "6060", &hints, &listp)
指的是 getaddrinfo("127.0.0.1", "6060", &hints, &listp)
,那么 localhost
和 127.0.0.1
不应该产生相同的结果吗?
您的循环假设列表中的所有条目都只是
AF_INET
/sockaddr_in
地址,但您没有为 hints.ai_family
设置值,因此您的 memset()
导致其被设置为 AF_UNSPEC
(0 ),这使得 getaddrinfo()
可以自由地返回其他地址类型,而不仅仅是 AF_INET
。
因此,在将
p->ai_family
转换为相应的 p->ai_addr
类型之前,您需要检查 sockaddr_...
。到目前为止,您拥有的输出代码仅当 p->ai_family
为 AF_INETand not something else, like say
AF_INET6` 时才合法。
请注意,在示例输出中,某些条目显示
sin_family: 30
,这绝对不是 AF_INET
(2)。 0.0.0.0
仅对 AF_INET
合法。在某些系统(例如 Apple)上,30 是 AF_INET6
。但 AF_INET6
在其他系统上有所不同(即 Linux 上为 10,Windows 上为 23,等等)。因此,这就是为什么您需要小心正确处理地址族,并为每个地址族使用正确的 sockaddr_...
结构。例如,如果您将 AF_INET6
误解为 AF_INET
,您可能会收到 IPv6 环回 IP ::1
,但错误地将其显示为 0.0.0.0
。
如果您只想强制输出
AF_INET
,则需要指定hints.ai_family = AF_INET
。否则,您需要更新代码以处理其他 AF_...
类型,而不仅仅是 AF_INET
,例如:
#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/un.h>
#define LISTENQ 10
int open_listenfd()
{
struct addrinfo hints, *listp, *p;
int s, listenfd, optval = 1;
char ipstr[INET6_ADDRSTRLEN+1];
// Get a list of potential server addresses
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC; // or AF_INET, AF_INET6, etc
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG | AI_NUMERICSERV;
s = getaddrinfo(NULL, "600", &hints, &listp);
if (s != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
exit(EXIT_FAILURE);
}
// Walk the list showing all IPs that were found
for(p = listp; p; p = p->ai_next) {
switch (p->ai_family) {
case AF_INET: {
struct sockaddr_in *addr_in = (struct sockaddr_in *) p->ai_addr;
printf("IP address: %s\n", inet_ntop(AF_INET, &(addr_in->sin_addr), ipstr, INET_ADDRSTRLEN));
printf("sin_family: %i\n", addr_in->sin_family);
printf("sin_port: %i\n", ntohs(addr_in->sin_port));
printf("\n");
}
break;
case AF_INET6: {
struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *) p->ai_addr;
printf("IP address: %s\n", inet_ntop(AF_INET6, &(addr_in6->sin6_addr), ipstr, INET6_ADDRSTRLEN));
printf("sin_family: %i\n", addr_in6->sin_family);
printf("sin_port: %i\n", ntohs(addr_in6->sin_port));
printf("\n");
}
break;
//...
}
}
// Walk the list looking for one that we can bind to
for(p = listp; p; p = p->ai_next) {
// Create a socket descriptor
if ((listenfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0)
continue;
// Eliminates "Address already in use" error from bind
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval, sizeof(int));
// Bind the descriptor to the address
if (bind(listenfd, p->ai_addr, p->ai_addrlen) == 0)
break;
close(listenfd); // Bind failed, try the next
}
if (p) {
// TODO: display which one was picked...
}
// Clean up
freeaddrinfo(listp);
if (!p) // No address worked
return -1;
// Make it a listening socket ready to accept connection request
if (listen(listenfd, LISTENQ) < 0) {
close(listenfd);
return -1;
}
return listenfd;
}
int main() {
int listenfd = open_listenfd();
printf("listenfd: %i\n", listenfd);
}