Sockets 使用IN6ADDR_SETV4映射和双堆栈套接字
这是本书的延续。我正在尝试使用双堆栈套接字,并试图了解setsockopt与IPV6_V6ONLY在哪些方面有用。关于链接的问题,有人建议我“如果还将服务器绑定到IPV6映射的IPv4地址,则仅将IPV6_v6设置为0会很有用”。我已经在下面完成了这项工作,并希望我的服务器能够接受来自IPv6和IPv4客户端的连接。但令人震惊的是,当我使用V4和V6套接字运行我的客户机时,两者都无法连接 有人能告诉我我做错了什么,或者我误解了IPv6的双栈功能吗 服务器:Sockets 使用IN6ADDR_SETV4映射和双堆栈套接字,sockets,tcp,ipv6,ipv4,Sockets,Tcp,Ipv6,Ipv4,这是本书的延续。我正在尝试使用双堆栈套接字,并试图了解setsockopt与IPV6_V6ONLY在哪些方面有用。关于链接的问题,有人建议我“如果还将服务器绑定到IPV6映射的IPv4地址,则仅将IPV6_v6设置为0会很有用”。我已经在下面完成了这项工作,并希望我的服务器能够接受来自IPv6和IPv4客户端的连接。但令人震惊的是,当我使用V4和V6套接字运行我的客户机时,两者都无法连接 有人能告诉我我做错了什么,或者我误解了IPv6的双栈功能吗 服务器: void ConvertToV4Map
void ConvertToV4MappedAddressIfNeeded(PSOCKADDR pAddr)
{
// if v4 address, convert to v4 mapped v6 address
if (AF_INET == pAddr->sa_family)
{
IN_ADDR In4addr;
SCOPE_ID scope = INETADDR_SCOPE_ID(pAddr);
USHORT port = INETADDR_PORT(pAddr);
In4addr = *(IN_ADDR*)INETADDR_ADDRESS(pAddr);
ZeroMemory(pAddr, sizeof(SOCKADDR_STORAGE));
IN6ADDR_SETV4MAPPED(
(PSOCKADDR_IN6)pAddr,
&In4addr,
scope,
port
);
}
}
addrinfo* result, hints;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
int nRet = getaddrinfo("powerhouse", "82", &hints, &result);
SOCKET sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
int no = 0;
if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&no, sizeof(no)) != 0)
return -1;
ConvertToV4MappedAddressIfNeeded(result->ai_addr);
if (bind(sock, result->ai_addr, 28/*result->ai_addrlen*/) == SOCKET_ERROR)
return -1;
if (listen(sock, SOMAXCONN) == SOCKET_ERROR)
return -1;
SOCKET sockClient = accept(sock, NULL, NULL);
printf("Got one!\n");
客户:
addrinfo* result, *pCurrent, hints;
char szIPAddress[INET6_ADDRSTRLEN];
memset(&hints, 0, sizeof hints); // Must do this!
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
const char* pszPort = "82";
if (getaddrinfo("powerhouse", "82", &hints, &result) != 0)
return -1;
SOCKET sock = socket(AF_INET, result->ai_socktype, result->ai_protocol);
int nRet = connect(sock, result->ai_addr, result->ai_addrlen);
我的C语言技能有些生疏,所以这里有一个用Python编写的反例。我的本地IPv4地址是37.77.56.75,因此我将绑定到该地址。我尽可能简单地把重点放在概念上 这是服务器端:
#/usr/bin/env python
导入套接字
#我们绑定到一个IPv6地址,其中包含一个IPv6-mapped-IPv4-address,
#端口5000,我们保留flowinfo(标识流的ID,未使用
#很多)和范围id(基本上是接口,如果使用
#链接本地地址)
主机='::ffff:37.77.56.75'
端口=5000
flowinfo=0
scopeid=0
sockaddr=(主机、端口、流信息、scopeid)
#创建IPv6套接字,将IPv6_V6ONLY设置为0并绑定到映射的地址
sock=socket.socket(socket.AF_INET6,socket.sock_流,0)
sock.setsockopt(socket.IPPROTO_IPV6,仅socket.IPV6,0)
sock.bind(sockaddr)
#收听并接受连接
短袜,听(0)
conn=sock.accept()
#打印远程地址
打印连接[1]
这里我们在代码中绑定到一个IPv6地址,但该地址实际上是一个IPv6映射的IPv4地址,因此实际上我们绑定到一个IPv4地址。这可以在查看例如netstat时看到:
$netstat-an | fgrep 5000
tcp4 0 37.77.56.75.5000**听
然后,我们可以使用IPv4客户端连接到此服务器:
#/usr/bin/env python
导入套接字
#连接到端口5000上的IPv4地址
主机='37.77.56.75'
端口=5000
sockaddr=(主机、端口)
#创建IPv4套接字并连接
sock=socket.socket(socket.AF\u INET,socket.sock\u流,0)
conn=sock.connect(sockaddr)
服务器将使用IPv6地址表示向我们显示已连接的用户:
('::ffff:37.77.56.76',50887,0,0)
在本例中,我从IPv4主机37.77.56.76
连接,它选择端口50887
进行连接
在本例中,我们只侦听IPv4地址(使用IPv6套接字,但它仍然是IPv4地址),因此仅IPv6的客户端将无法连接。同时具有IPv4和IPv6的客户端当然可以使用具有IPv6映射IPv4地址的IPv6套接字,但这样它就不会真正使用IPv6,而只是IPv4连接的IPv6表示
双堆栈服务器必须:
#我们绑定到通配符IPv6地址,这将使操作系统在这两个端口上都侦听
#IPv4和IPv6
主机=':'
端口=5000
flowinfo=0
scopeid=0
sockaddr=(主机、端口、流信息、scopeid)
“我的Mac OS X”框显示如下:
$netstat-an | fgrep 5000
tcp46 0*.0.5000*
请注意tcp46
,它指示它侦听两个地址族。不幸的是,在Linux上,它只显示tcp6
,即使在两个系列上都侦听时也是如此
现在来看最复杂的示例:侦听多个套接字
#/usr/bin/env python
导入选择
导入套接字
#我们绑定到一个IPv6地址,该地址包含一个IPv6映射的IPv4地址
sockaddr1=('::ffff:37.77.56.75',5001,0,0)
sock1=socket.socket(socket.AF_INET6,socket.SOCK_流,0)
sock1.setsockopt(socket.IPPROTO_IPV6,仅socket.IPV6,0)
sock1.bind(sockaddr1)
1.听(0)
#我们绑定到一个真正的IPv6地址
sockaddr2=('2a00:8640:1::224:36ff:feef:1d89',5001,0,0)
sock2=socket.socket(socket.AF_INET6,socket.SOCK_流,0)
sock2.bind(sockaddr2)
sock2.听(0)
#选择激活的套接字
套接字=[sock1,sock2]
可读、可写、异常=选择。选择(套接字,[],套接字)
对于插入式可读:
#接受连接
conn=sock.accept()
#打印远程地址
打印连接[1]
运行此示例时,两个套接字都可见:
$netstat-an | fgrep 5000
tcp6 0 2a00:8640:1::224.5000**听
tcp4 0 37.77.56.75.5000**听
现在,只有IPv6的客户端才能连接到
2a00:8640:1::224:36ff:feef:1d89
,只有IPv4的客户端才能连接到37.77.56.75
。双栈客户端可以选择要使用的协议。您是否尝试过设置hits.ai\u family=AF\u INET代码>到提示.ai_family=AF_INET6代码>正如我现在看到的,您正在查找IPv4通配符地址,而不是IPV6不确定您在那里的意思;当我传递机器名称(“发电站”)时,我没有查找通配符地址……除非我误解了你??啊,我不记得节点名称覆盖了AI_被动标志。AI_被动被忽略,因此您应该删除它以使代码更清晰。发电厂房解决了什么问题(如果您键入主机发电厂房)?我在Windows上,所以“主机”不起作用:)尽管如此,我还是听到了一些好消息