IPv6 与 IPv4 是不兼容的, 但如果服务器使用 IPv6, 客户端使用 IPv4 也可以正常连接, 因为 IPv4 的地址会自动映射为 IPv6 的地址, 像 ::ffff:192.168.27.25
(1) 创建 Socket 函数: socket
(2) 从应用层 sockaddr 传递至 内核 的相关函数: bind, connect, sendto
(3) 从内核传递 sockaddr 至 应用层 的相关函数: accept, recvfrom, getpeername, getsockname
不要再像这样去创建 Socket, socket(AF_INET, SOCK_STREAM, 0);
按 man 文档比较好的做法是调用 getaddrinfo 函数, 并获取 addrinfo
结构中的对应字段作为 socket()
的参数
如下所示:
struct addrinfo hints;
struct addrinfo *res, *p;
int sfd = -1;
memset(&hints, 0, sizeof(hints));
hints.ai_family = family;
hints.ai_socktype = SOCK_STREAM; /* Stream socket */
hints.ai_protocol = IPPROTO_TCP; /* TCP protocol */
int ret = getaddrinfo(host, service, &hints, &res);
if( ret != 0 ) {
LOG_ERR("getaddrinfo() failed : %s", gai_strerror(ret));
return -1;
}
/*
Try open socket with each address getaddrinfo returned,
until we get a valid listening socket.
*/
for(p = res; p != NULL; p = p->ai_next) {
if( (sfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1 ) {
LOG_ERRNO("socket() failed");
continue;
}
int on = 1;
if( setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0 )
{
LOG_ERRNO("setsockopt() - SO_REUSEADDR failed");
goto _failed_listen_socket;
}
if( bind(sfd, p->ai_addr, p->ai_addrlen) == -1 ) {
LOG_ERRNO("bind() failed :");
goto _failed_listen_socket;
}
break;
_failed_listen_socket:
close(sfd);
continue;
}
freeaddrinfo(res);
完整代码: dual_stack_server.c
传递给sockaddr *
参数的结构使用struct sockaddr_storage
,有些代码会使用sockaddr_in6
或sockaddr_in
.
(1) 关于IPV6_V6ONLY
Windows 默认的 IPV6_V6ONLY 设置为 true, 所以在 Windows 下如果需要同时监听 IPv6 和 IPv4 的话那么需要将它设置为 false.