这是我的案例:
我从 https://whatismyipaddress.com/ 获取了我的公共 IPv6 和公共 IPv4,就像:
IPv6:2603:6080:1001:100:2042:----:----,
IPv4:75.177.130.***。
然后我使用下面的代码作为TCP/IP服务器程序:
我可以将listendfd与IPv6地址绑定,并且可以从运行TCP/IP客户端程序的同一台或另一台计算机与其建立连接(设置hints.ai_family = AF_INET6),
但我无法将listendfd与IPv4地址绑定,也无法连接到它(设置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 路由器。这就是为什么你的电脑无法绑定到它。
如果您想监听 IPv6 地址:
收听 IPv4:
我希望这能澄清差异