提问人:Mory 提问时间:9/17/2023 最后编辑:MatMory 更新时间:9/17/2023 访问量:49
为什么我不能将 listenfd 与我的公共 IPv4 地址绑定,但我可以用我的公共 IPv6 来绑定?
Why couldn't I bind the listenfd with my public IPv4 address, but I could do that with my public IPv6?
问:
这是我的情况:
我从 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客户端程序的另一台计算机与它建立连接。
谁能向我解释为什么会这样?欢迎任何评论!谢谢!
答:
最大的区别是 IPv4 通常使用 NAT,而 IPv6 则不。这意味着您的 IPv6 地址实际上属于您的 PC/手机/平板电脑等(每个人都有数十亿个 IPv6 地址,因此每个设备都有自己的地址),而您的 IPv4 地址(您通常只得到一个)属于您的 NAT 路由器。这就是您的 PC 无法绑定到它的原因。
如果要侦听 IPv6 地址:
- 找出您的权益本地地址,使用 IPv6,您可以获得多个地址。您通常有一个或多个短期地址来保护隐私,以及一个稳定的地址。你通常想听后者
- 绑定到您的本地地址(或者只收听所有地址,更轻松!
- 在路由器中配置防火墙以允许传入连接
要侦听 IPv4:
- 了解您的公共地址是什么
- 找出您的内部(私人)地址是什么
- 监听内部地址
- 将 NAT 路由器配置为将公共地址的特定端口转发到内部地址上的特定端口
- 在路由器中配置防火墙以允许传入连接
我希望这能澄清其中的区别
评论