将IPv4客户端连接到IPv6服务器:连接被拒绝

时间:2013-05-10 10:57:01

标签: c++ sockets ipv6 ipv4

我正在试验IPv6套接字,特别是Windows Vista及更高版本提供的“双栈”功能,默认情况下显然在Unix上。我发现当我将服务器绑定到特定的IP地址或本地计算机的主机名解析时,我无法接受来自IPv4客户端的连接。但是当我绑定到INADDR_ANY时,我可以。

请考虑我的服务器的以下代码。您可以看到我遵循Microsoft的建议创建IPv6套接字,然后将IPV6_V6ONLY标志设置为零:

addrinfo* result, *pCurrent, hints;

memset(&hints, 0, sizeof hints);    // Must do this!
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;     // We intend to use the addrinfo in a call to connect().  (I know it is ignored if we specify a server to connect to...)

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;

if (bind(sock, result->ai_addr, result->ai_addrlen) ==  SOCKET_ERROR)
    return -1;

if (listen(sock, SOMAXCONN) == SOCKET_ERROR)
    return -1;

SOCKET sockClient = accept(sock, NULL, NULL);

这是我客户的代码。您可以看到我创建了一个IPv4套接字并尝试连接到我的服务器:

addrinfo* result, *pCurrent, hints;

memset(&hints, 0, sizeof hints);    // Must do this!
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;

if (getaddrinfo("powerhouse", "82", &hints, &result) != 0)
    return -1;

SOCKET sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
int nRet = connect(sock, result->ai_addr, result->ai_addrlen);

我的连接呼叫的结果始终是10061:连接被拒绝。

如果我将服务器代码更改为绑定到::(或将NULL主机传递给getaddrinfo()(同样的事情)),并更改我的客户端代码以在getaddrinfo()调用中指定NULL主机,那么V4客户端可以正常连接。

有谁可以解释为什么?如果我们想要双插槽行为,我还没有读过任何我们必须指定NULL主机(因此使用INADDR_ANY)的内容。这不是一个要求,因为我有一个多宿主主机,我只想在一些可用的IP上接受IPv4?

编辑15/05/2013:

这是让我对代码失败原因感到困惑的相关文档:

来自Dual-Stack Sockets for IPv6 Winsock Applications

  

“Windows Vista及更高版本提供了创建单个IPv6的功能   套接字,可以处理IPv6和IPv4流量。例如,TCP   创建IPv6侦听套接字,进入双栈模式,然后   绑定到端口5001.这个双栈插槽可以接受来自的连接   连接到端口5001和IPv4 TCP客户端的IPv6 TCP客户端   连接到端口5001。“

     

“默认情况下,在Windows Vista及更高版本上创建的IPv6套接字   通过IPv6协议运行。为了使IPv6套接字成为   在双栈套接字中,必须使用调用setsockopt函数   IPV6_V6ONLY套接字选项,用于在套接字之前将此值设置为零   绑定到IP地址。 设置IPV6_V6ONLY套接字选项时   为零,可以使用为AF_INET6地址族创建的套接字   向IPv6地址或IPv4发送和接收数据包   映射地址。(强调我的)“

2 个答案:

答案 0 :(得分:8)

IPv4和IPv6是两个独立的协议。无法使用其他协议处理一个协议的数据包。这就是双栈概念存在的原因:您的系统同时运行IPv4和IPv6协议栈,同时具有IPv4和IPv6地址等。

操作系统有一个技巧,您可以拥有一个侦听所有IPv4和IPv6地址的IPv6套接字。您仍然需要在主机上同时具有两个地址系列,并且仅在绑定到通配符地址时才有效。将该套接字绑定到不再起作用的固定地址后,它将仅适用于您绑定的地址。

因此,如果要监听所有可用地址,则将IPV6_V6ONLY设置为0并监听通配符地址。 IPv4客户端将显示为使用以::ffff:开头的IPv6地址,最后32位包含IPv4地址。

如果要绑定到特定地址,则需要将套接字绑定到要侦听的每个地址。然后你需要使用ie select(...)监视这些套接字并响应那些因为有人连接它们而变得活跃的套接字。

答案 1 :(得分:2)

此链接http://www.masterraghu.com/subjects/np/introduction/unix_network_programming_v1.3/ch12lev1sec2.html提供有关IPv4和IPv6连接的更多信息,

  

大多数双栈主机在处理时应使用以下规则   听套接字:

     
      
  • 侦听IPv4套接字只能接受来自IPv4客户端的传入连接。
  •   
  • 如果服务器具有绑定通配符地址的侦听IPv6套接字,并且未设置IPV6_V6ONLY套接字选项(第7.8节),   该套接字可以接受来自任一IPv4客户端的传入连接   或IPv6客户端。对于来自IPv4客户端,服务器的连接   连接的本地地址将是相应的IPv4映射   IPv6地址。
  •   
  • 如果服务器具有侦听IPv6套接字,该套接字绑定了除IPv4映射的IPv6地址以外的IPv6地址,或者绑定了   通配符地址但已设置IPv6_V6ONLY套接字选项(Section   7.8),该套接字只能接受来自IPv6客户端的传入连接。
  •