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

Wad*_*Wad 6 c++ sockets ipv4 ipv6

我正在试验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);
Run Code Online (Sandbox Code Playgroud)

这是我的客户的代码.您可以看到我创建了一个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);
Run Code Online (Sandbox Code Playgroud)

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

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

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

编辑15/05/2013:

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

来自用于IPv6 Winsock应用程序的双栈套接字

"Windows Vista及更高版本提供了创建单个IPv6套接字的功能,可以同时处理IPv6和IPv4流量.例如,创建用于IPv6的TCP侦听套接字,进入双堆栈模式,并绑定到端口5001.堆栈套接字可以接受来自连接到端口5001的IPv6 TCP客户端和连接到端口5001的IPv4 TCP客户端的连接."

"默认情况下,在Windows Vista上创建的IPv6套接字仅在IPv6协议上运行.为了将IPv6套接字转换为双栈套接字,必须使用IPV6_V6ONLY套接字选项调用setsockopt函数,以将此值设置为零点前的套接字绑定到一个IP地址.当IPV6_V6ONLY套接字选项设置为零,为AF_INET6地址族创建的套接字可用于发送和接收数据包,并从IPv6地址或IPv4映射地址.(强调我的)"

San*_*ann 8

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

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

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

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

  • 如果绑定到catch-all地址,这确实有效.您将服务器绑定到特定的真实IPv6地址.但是客户端不能,因为仅IPv4客户端需要IPv4目标地址才能连接,并且由于仅将服务器绑定到IPv6地址而不存在.如果希望服务器接受IPv6套接字上的IPv4连接,则除了绑定到的全局IPv6地址外,还需要将服务器绑定到服务器的IPv6映射IPv4地址.您*需要*为客户端提供IPv4地址以进行连接. (2认同)