正在连接IPv4客户端到IPv6服务器:连接拒绝
Connecting IPv4 client to IPv6 server: connection refused
我正在尝试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);
我的connect调用的结果总是10061:connection refused.
如果我将服务器代码更改为绑定到::(或将NULL主机传递给getaddrinfo()(同样的事情)),并更改客户端代码以在getaddrinfo()调用中指定NULL主机,那么V4客户端可以正常连接。
谁能解释一下为什么?我没有读到任何东西,我们必须指定一个NULL主机(因此使用INADDR_ANY),如果我们想要双套接字行为。这不能是一个要求,因为我有一个多主主机,我想接受IPv4只在一些可用的ip ?
编辑15/05/2013:
这是相关的文档,它让我对为什么我的代码失败感到困惑:
来自IPv6 Winsock应用程序的双栈套接字
Windows Vista及以后版本提供了创建单个IPv6的能力套接字,可以处理IPv6和IPv4流量。例如TCP创建IPv6监听套接字,将其置于双栈模式,并且绑定端口5001。这个双栈套接字可以接受来自IPv6 TCP客户端连接到端口5001和IPv4 TCP客户端连接到端口5001.""默认情况下,仅在Windows Vista及更高版本上创建的IPv6套接字在IPv6协议上运行。为了使IPv6套接字变成对于双栈套接字,setsockopt函数必须使用IPV6_V6ONLY套接字选项将此值设置为零已绑定IP地址。设置IPV6_V6ONLY套接字选项时到0时,可以使用为AF_INET6地址族创建的套接字发送和接收来自IPv6地址或IPv4地址的数据包映射的地址。(强调我的)"
IPv4和IPv6是两个独立的协议。一种协议的报文不能被另一种协议处理。这就是双栈概念存在的原因:你的系统同时运行IPv4和IPv6协议栈,同时拥有IPv4和IPv6地址,等等。
操作系统有一个技巧,你可以有一个IPv6套接字监听所有IPv4和IPv6地址。您仍然需要在主机上拥有两个地址族,并且只有在绑定到通配符地址时才有效。一旦你将套接字绑定到一个固定的地址,这个地址就不再工作了,它只会为你绑定到的地址工作。
所以如果你想监听所有可用的地址,那么将IPV6_V6ONLY设置为0并监听通配符地址即可。IPv4客户端将显示为使用以::ffff:
开头的IPv6地址,最后32位包含IPv4地址。
当你想要绑定到特定的地址时,你需要将套接字绑定到你想要监听的每个地址。然后,您需要使用select(...)
来监视这些套接字,并响应那些由于有人连接到它们而变得活跃的套接字。
此链接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套接字,该套接字绑定了一个IPv6地址,而不是ipv4映射的IPv6地址通配符地址,但设置了IPv6_V6ONLY套接字选项
- QTcpSocket在不阻塞GUI的情况下重新连接到服务器
- C++套接字客户端到 Python 服务器未创建连接
- 计算出有多少客户端可以连接到我正在使用的一些tcp服务器代码
- 在 QNX 中,如何管理服务器和客户端之间的 IPC 连接?
- QTcpSocket 在 RemoteHostClosedError 后重新连接到服务器时无法传输数据
- C++ Winsock2 客户端未通过远程 IP 连接到服务器
- iocp openssl 对等服务器在与 ConnectEx 连接后关闭连接
- boost beast websocket服务器也接受http连接
- 带有openSSL的libwebsocket服务器不接受连接
- 检测grpc服务器中关闭的客户端连接
- QAbstractSocket 从服务器端关闭连接时的奇怪行为
- 仅通过建立一次TCP连接将Recv从客户端发送到服务器套接字
- C++ WebSocket 帧发送会导致服务器关闭连接
- 为什么我的客户端无法连接到服务器?
- 将移动设备(iOS和Android)连接到带有QT / C++和镜像显示的VNC服务器
- 我正在编写一个简单的客户端套接字应用程序,但在连接后服务器收到一个空缓冲区
- 提升 ASIO 服务器 - 检测空闲连接
- C++ 套接字 read() 有时在连接到服务器时返回 -1
- "MFC/C++ Socket programming.." 如何连接服务器和客户端?
- 无法使用LDAP SSL连接服务器