将IPv4客户端连接到IPv6服务器:连接被拒绝 [英] Connecting IPv4 client to IPv6 server: connection refused

查看:1416
本文介绍了将IPv4客户端连接到IPv6服务器:连接被拒绝的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



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

  addrinfo *结果, * pCurrent,提示; 

memset(& hints,0,sizeof提示); //必须这样做!
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; //我们打算在对connect()的调用中使用addrinfo。 (我知道,如果我们指定要连接的服务器...,它将被忽略。)

int nRet = getaddrinfo( powerhouse, 82,& hints,& result);

SOCKET袜子=套接字(AF_INET6,SOCK_STREAM,IPPROTO_TCP);

int no = 0;
if(setsockopt(sock,IPPROTO_IPV6,IPV6_V6ONLY,(char *)& no,sizeof(no))!= 0)
返回-1;

如果(bind(sock,result-> ai_addr,result-> ai_addrlen)== SOCKET_ERROR)
返回-1;

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

SOCKET sockClient = accept(袜子,NULL,NULL);

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

  addrinfo *结果,* pCurrent,提示; 

memset(& hints,0,sizeof提示); //必须这样做!
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;

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

SOCKET袜子=套接字(结果-> ai_family,result-> ai_socktype,result-> ai_protocol);
int nRet = connect(袜子,结果-> ai_addr,结果-> ai_addrlen);

我的connect呼叫的结果始终是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套接字成为
a双栈插槽,setsockopt必须在套接字
绑定到IP地址之前,使用
IPV6_V6ONLY套接字选项调用该函数,以将此值设置为零。 当IPV6_V6ONLY套接字选项将
设置为零时,可以使用
为AF_INET6地址族创建的套接字来往IPv6地址或IPv4
收发数据包。映射的地址。
(重点是我的)



解决方案

IPv4和IPv6是两个一个协议的数据包不能使用另一协议处理,这就是为什么存在双协议栈的原因:您的系统同时运行IPv4和IPv6协议栈,同时具有IPv4和IPv6地址,等等。



操作系统有一个技巧,您可以拥有一个侦听所有IPv4和IPv6地址的IPv6套接字,您仍然需要在主机上同时拥有两个地址族,并且仅当您绑定时



因此,将套接字绑定到不再可用的固定地址后,它仅适用于已绑定的地址。



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



要绑定到特定地址时,您需要将套接字绑定到要监听的每个地址。然后,您需要使用 select(...)来监视那些套接字并响应由于有人连接而变为活动状态的那些套接字。


I am experimenting with IPv6 sockets, particularly the "dual stack" capability offered on Windows Vista and later, and apparently on Unix by default. I am finding that when I bind my server to a specific IP address, or to the hostname resolution of my local machine, I cannot accept a connection from an IPv4 client. When I bind to INADDR_ANY however, I can.

Please consider the following code for my server. You can see that I follow Microsoft's advice of creating an IPv6 socket, then setting the IPV6_V6ONLY flag to zero:

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);

Here is the code for my client. You can see I create an IPv4 socket and attempt to connect to my server:

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);

The result from my connect call is always 10061: connection refused.

If I change my server code to bind to :: (or pass a NULL host to getaddrinfo() (same thing)), and change my client code to specify a NULL host in the getaddrinfo() call, then the V4 client can connect fine.

Can anyone explain why please? I have not read anything that we must specify a NULL host (hence use INADDR_ANY) if we want dual-socket behaviour. This can't be a requirement, because what I have a multihomed host and I want to accept IPv4 on only some of the available IPs?

EDIT 15/05/2013:

This is the relevant documentation which has gotten me confused as to why my code fails:

From Dual-Stack Sockets for IPv6 Winsock Applications

"Windows Vista and later offer the ability to create a single IPv6 socket which can handle both IPv6 and IPv4 traffic. For example, a TCP listening socket for IPv6 is created, put into dual stack mode, and bound to port 5001. This dual-stack socket can accept connections from IPv6 TCP clients connecting to port 5001 and from IPv4 TCP clients connecting to port 5001."

"By default, an IPv6 socket created on Windows Vista and later only operates over the IPv6 protocol. In order to make an IPv6 socket into a dual-stack socket, the setsockopt function must be called with the IPV6_V6ONLY socket option to set this value to zero before the socket is bound to an IP address. When the IPV6_V6ONLY socket option is set to zero, a socket created for the AF_INET6 address family can be used to send and receive packets to and from an IPv6 address or an IPv4 mapped address. (emphasis mine)"

解决方案

IPv4 and IPv6 are two separate protocols. Packets of one protocol cannot be handled using the other protocol. That is why the concept of Dual Stack exists: your system runs both the IPv4 and IPv6 protocol stacks, has both IPv4 and IPv6 addresses, etc.

Operating systems have a trick where you can have an IPv6 socket that listens on all IPv4 and IPv6 addresses. You still need to have both address families on the host, and it only works when you bind to the wildcard address. Once you bind that socket to a fixed address that doesn't work anymore and it will only work for the address that you have bound to.

So if you want to listen on all available addresses then setting IPV6_V6ONLY to 0 and listening on the wildcard address work. The IPv4 clients will be shown as using IPv6 addresses starting with ::ffff: with the last 32 bits containing the IPv4 address.

When you want to bind to specific addresses you will need sockets bound to each of the addresses you want to listen on. Then you need to use i.e. select(...) to monitor those sockets and to respond to those that become active because someone connects to them.

这篇关于将IPv4客户端连接到IPv6服务器:连接被拒绝的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆