为什么我不能将 listenfd 与我的公共 IPv4 地址绑定,但我可以用我的公共 IPv6 来绑定?

Why couldn't I bind the listenfd with my public IPv4 address, but I could do that with my public IPv6?

提问人:Mory 提问时间:9/17/2023 最后编辑:MatMory 更新时间:9/17/2023 访问量:49

问:

这是我的情况:

我从 https://whatismyipaddress.com/ 获得了我的公共 IPv6 和公共 IPv4,它们如下所示:

IPv6:2603:6080:1001:100:2042:----:----

IPv4:75.177.130.***。

然后,我使用下面的代码作为 TCP/IP 服务器程序:

我可以将 listendfd 与 IPv6 地址绑定,并且可以从运行 TCP/IP 客户端程序的同一台或另一台计算机与它建立连接(set hints.ai_family = AF_INET6),

但是我无法使用 IPv4 地址绑定 listendfd,也无法连接到它(set hints.ai_family = 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
#define MAXLINE 8000

int open_listenfd(char *port)
{
    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_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; 
    hints.ai_flags |= AI_NUMERICSERV;
    hints.ai_family = AF_INET;

    s = getaddrinfo("75.177.130.***", port, &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) {

        switch(p->ai_family) {
            case AF_INET: {
                struct sockaddr_in *addr_in = (struct sockaddr_in *)p->ai_addr;
                printf("bind: family=AF_INET addr=%s port=%u\n",
                   inet_ntoa(addr_in->sin_addr),
                   ntohs(addr_in->sin_port));
                break; 
            }
            case AF_INET6: {
                struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *) p->ai_addr;
                printf("bind: family=AF_INET6 IP address: %s\n", inet_ntop(AF_INET6, &(addr_in6->sin6_addr), ipstr, INET6_ADDRSTRLEN));
                printf("Port: %i\n", ntohs(addr_in6->sin6_port)); 
                break;
            }
        }

        // 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
        // NULL coundn't bind port 8080, which might have been used by other services 
        if(bind(listenfd, p->ai_addr, p->ai_addrlen) == 0)
            break;

        fprintf(stderr, "bind error: %s\n", gai_strerror(s));
        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 lisfd, connfd;
    socklen_t clientlen;
    struct sockaddr_storage clientaddr;
    char client_hostname[MAXLINE], client_port[MAXLINE];

    char *listenport = "8798";
    lisfd = open_listenfd(listenport);
    printf("listenfd: %i\n", lisfd);

    while(1) {
        clientlen = sizeof(clientaddr);
        connfd = accept(lisfd, (struct sockaddr *)&clientaddr, &clientlen);
        if (connfd != -1) {
            getnameinfo((struct sockaddr *)&clientaddr, clientlen, client_hostname, MAXLINE, client_port, MAXLINE, 0);
            printf("accepted connection from(%s %s)\n", client_hostname, client_port);
            close(connfd);
        }
    }
}

顺便说一句,我可以使用我计算机的私有 IPv4 地址 192.168.0.1* 进行绑定,并让我的 TCP/IP 客户端程序在我家中运行另一台私有 IPv4 地址为 192.168.0.5* 的计算机来连接它。

但是,如果我的一台计算机使用iPhone的连接,则计算机会生成不同的私有IPv4地址,例如172.20.**.*,并且我无法从我家中运行TCP / IP客户端程序的另一台计算机与它建立连接。

谁能向我解释为什么会这样?欢迎任何评论!谢谢!

C 套接字绑定 IPv6 IPv4

评论

0赞 Some programmer dude 9/17/2023
也许有人已经在特定接口上使用该端口?你得到的错误是什么?
0赞 stark 9/17/2023
绑定后打印了错误的值。使用 perror 打印 errno 的值。
0赞 dbush 9/17/2023
公共 IPv6 地址是否在本地分配给您的计算机?
0赞 Mory 9/17/2023
谢谢大家的评论!致一些程序员家伙和斯塔克:我用perro打印了错误值,它说:无法分配请求的地址。致 dbush:我不认为我从 whatismyipaddress.com 本地分配给我的机器的公共 IPv6 地址,因为几乎每次我使用该网站检查我的公共 IP 地址时,IPv6 地址都会更改,但来自该网站的公共 IPv4 地址不会更改,仍然像 75.177.130。
1赞 Barmar 9/17/2023
如果您的计算机位于 NAT 路由器后面,则不会绑定到公共地址,而是绑定到专用地址。路由器在执行端口转发时将公共地址转换为专用地址。

答:

0赞 Sander Steffann 9/17/2023 #1

最大的区别是 IPv4 通常使用 NAT,而 IPv6 则不。这意味着您的 IPv6 地址实际上属于您的 PC/手机/平板电脑等(每个人都有数十亿个 IPv6 地址,因此每个设备都有自己的地址),而您的 IPv4 地址(您通常只得到一个)属于您的 NAT 路由器。这就是您的 PC 无法绑定到它的原因。

如果要侦听 IPv6 地址:

  • 找出您的权益本地地址,使用 IPv6,您可以获得多个地址。您通常有一个或多个短期地址来保护隐私,以及一个稳定的地址。你通常想听后者
  • 绑定到您的本地地址(或者只收听所有地址,更轻松!
  • 在路由器中配置防火墙以允许传入连接

要侦听 IPv4:

  • 了解您的公共地址是什么
  • 找出您的内部(私人)地址是什么
  • 监听内部地址
  • 将 NAT 路由器配置为将公共地址的特定端口转发到内部地址上的特定端口
  • 在路由器中配置防火墙以允许传入连接

我希望这能澄清其中的区别

评论

0赞 Mory 9/17/2023
非常感谢您的详细解释,我很感激!还有一个问题:每个人如何获得数十亿个 IPv6 地址?你是说数十亿个公共 IPv6 地址吗(几乎每次我用 whatismyipaddress.com 查找我的 IP 地址时,IPV6 都不同,IPv4 75.177.130。
0赞 Mory 9/18/2023
顺便说一句,1)我在连接到家庭网络的一台计算机上运行了我的 TCP/IP 服务器程序,并在连接到我的手机网络的另一台计算机上运行了我的 TCP/IP 客户端程序,他们无法连接。2) 我运行 TCP/IP 服务器,客户端程序在连接到我的家庭网络的相同/不同计算机上运行,它们可以连接。所以,我认为正如你所说,如果我想允许来自家庭网络外部的传入连接,我需要在我的路由器中配置防火墙。(路由器可能有一个默认设置,只允许传出到 Internet 的连接。