为什么 getaddrinfo("localhost", "600", &hints, &listp) 创建两个套接字地址,分别是 0.0.0.0 和 127.0.0.1?

问题描述 投票:0回答:1

有人可以在下面的代码中向我解释一下为什么吗:

  • 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
不应该产生相同的结果吗?

sockets networking port
1个回答
0
投票

您的循环假设列表中的所有条目都只是

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_INET
and 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);
}
© www.soinside.com 2019 - 2024. All rights reserved.