Bind() Windows 套接字适用于 IPv4 但不适用于 IPv6
Bind() Windows Socket works with IPv4 but not with IPv6
我正在学习如何使用 Winsock。我正在编写一个监听连接的服务器。一切正常,直到我想将整个代码更改为 IPv6。
我浏览了与该主题相关的所有问题和网站,但没有适合我的解决方案...:/
背景:Windows,使用 Visual Studio 2019,IPv6 已激活并且工作正常(例如,我可以在 IPv6 中 ping 我的 phone)。
每次我收到错误 10049。这个告诉我给定的 IP 无效。我尝试了在 ipconfig
和 ::1
.
中可以找到的每一个
int slisten;
long res;
SOCKET client;
WSADATA wsaData;
res = WSAStartup(MAKEWORD(2, 1), &wsaData);
if (res == 0)
cout << "WSAStartup()\t\t successful" << endl;
else
cout << "error WSAStartup(): " << WSAGetLastError() << endl;
slisten = socket(PF_INET6, SOCK_STREAM, 0);
if (slisten != INVALID_SOCKET)
cout << "socket() \t\t successful" << endl;
else
cout << "error socket(): " << WSAGetLastError() << endl;
//IPv6
struct sockaddr_in6 ip6addr;
ip6addr.sin6_family = AF_INET6;
ip6addr.sin6_port = htons(55339);
inet_pton(AF_INET6, "::1", &ip6addr.sin6_addr);
res = bind(slisten, (struct sockaddr*) & ip6addr, sizeof ip6addr);
if (res == SOCKET_ERROR)
{
wprintf(L"bind function failed with error: %d\n", WSAGetLastError());
}
/* IPv4, not in use
struct sockaddr_in ip4addr;
ip4addr.sin_family = AF_INET;
ip4addr.sin_port = htons(3490);
inet_pton(AF_INET, "127.0.0.1", &ip4addr.sin_addr);
res = bind(slisten, (struct sockaddr*) & ip4addr, sizeof ip4addr);
if (res == SOCKET_ERROR)
{
wprintf(L"bind function failed with error: %d\n", WSAGetLastError());
}
*/
IPv4 方法有效,但 IPv6 无效。
WSAStartup() = 成功
socket() = 成功
绑定() = 错误 10049
如果我把IPv6部分注释掉,IPv4部分取消注释,在slisten = socket()
中把PF_INET6
改成PF_INET
,就没有bind()
错误了。
如果您需要更多代码或其他内容,请告诉我,但我的意思是,如果程序在该特定函数处抛出错误,那么在此之前的一切都必须正常工作,因为没有错误 (?)。
部分代码属于 YouTube 上的 s41b0tproductions。你可以在 https://www.youtube.com/user/s41b0tproductions/videos.
找到他
调用socket()
时,PF_INET
/PF_INET6
应该是AF_INET
/AF_INET6
。但这通常无关紧要,因为 PF_...
和 AF_...
宏通常映射到相同的数值。
但是,更重要的是,在填充 sockaddr_in
/sockadr_in6
结构之前,您没有将它们清零。在使用它们之前,您应该始终将 Win32 API 结构清零。对于 AF_INET
,您没有用零填充 sockaddr_in::sin_zero
字段,因此它将包含来自调用堆栈的随机字节。这通常没问题,因为该字段通常会被忽略。但是对于 AF_INET6
,您没有填充 sockaddr_in6::sin6_flowinfo
和 sockaddr_in6::sin6_scope_id
字段,它们确实有意义,并且当它们包含随机值时可能是 WSAEADDRNOTAVAIL
错误的根本原因.
改用这个:
struct sockaddr_in6 ip6addr = {};
struct sockaddr_in ip4addr = {};
或:
struct sockaddr_in6 ip6addr;
ZeroMemory(&ip6addr, sizeof ip6addr);
struct sockaddr_in ip4addr;
ZeroMemory(&ip4addr, sizeof ip4addr);
但是,为 bind()
(和 connect()
)创建正确填充的 sockaddr_in
/sockaddr_in6
结构的更好方法是使用 getaddrinfo()
而不是手动填充它们:
const char szHost = "::1"; // or "127.0.0.1"
const char *szPort = "55339";
addrinfo hint = {};
hint.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
hint.ai_family = AF_INET6; // or AF_INET
hint.ai_socktype = SOCK_STREAM;
hint.ai_protocol = IPPROTO_TCP;
addrinfo *addr = NULL;
res = getaddrinfo(szHost, szPort, &hint, &addr);
if (res != 0)
{
wprintf(L"getaddrinfo function failed with error: %d\n", res);
}
else
{
res = bind(slisten, addr->ai_addr, addr->ai_addrlen);
if (res == SOCKET_ERROR)
{
wprintf(L"bind function failed with error: %d\n", WSAGetLastError());
}
freeaddrinfo(addr);
}
我正在学习如何使用 Winsock。我正在编写一个监听连接的服务器。一切正常,直到我想将整个代码更改为 IPv6。
我浏览了与该主题相关的所有问题和网站,但没有适合我的解决方案...:/
背景:Windows,使用 Visual Studio 2019,IPv6 已激活并且工作正常(例如,我可以在 IPv6 中 ping 我的 phone)。
每次我收到错误 10049。这个告诉我给定的 IP 无效。我尝试了在 ipconfig
和 ::1
.
int slisten;
long res;
SOCKET client;
WSADATA wsaData;
res = WSAStartup(MAKEWORD(2, 1), &wsaData);
if (res == 0)
cout << "WSAStartup()\t\t successful" << endl;
else
cout << "error WSAStartup(): " << WSAGetLastError() << endl;
slisten = socket(PF_INET6, SOCK_STREAM, 0);
if (slisten != INVALID_SOCKET)
cout << "socket() \t\t successful" << endl;
else
cout << "error socket(): " << WSAGetLastError() << endl;
//IPv6
struct sockaddr_in6 ip6addr;
ip6addr.sin6_family = AF_INET6;
ip6addr.sin6_port = htons(55339);
inet_pton(AF_INET6, "::1", &ip6addr.sin6_addr);
res = bind(slisten, (struct sockaddr*) & ip6addr, sizeof ip6addr);
if (res == SOCKET_ERROR)
{
wprintf(L"bind function failed with error: %d\n", WSAGetLastError());
}
/* IPv4, not in use
struct sockaddr_in ip4addr;
ip4addr.sin_family = AF_INET;
ip4addr.sin_port = htons(3490);
inet_pton(AF_INET, "127.0.0.1", &ip4addr.sin_addr);
res = bind(slisten, (struct sockaddr*) & ip4addr, sizeof ip4addr);
if (res == SOCKET_ERROR)
{
wprintf(L"bind function failed with error: %d\n", WSAGetLastError());
}
*/
IPv4 方法有效,但 IPv6 无效。
WSAStartup() = 成功
socket() = 成功
绑定() = 错误 10049
如果我把IPv6部分注释掉,IPv4部分取消注释,在slisten = socket()
中把PF_INET6
改成PF_INET
,就没有bind()
错误了。
如果您需要更多代码或其他内容,请告诉我,但我的意思是,如果程序在该特定函数处抛出错误,那么在此之前的一切都必须正常工作,因为没有错误 (?)。
部分代码属于 YouTube 上的 s41b0tproductions。你可以在 https://www.youtube.com/user/s41b0tproductions/videos.
找到他调用socket()
时,PF_INET
/PF_INET6
应该是AF_INET
/AF_INET6
。但这通常无关紧要,因为 PF_...
和 AF_...
宏通常映射到相同的数值。
但是,更重要的是,在填充 sockaddr_in
/sockadr_in6
结构之前,您没有将它们清零。在使用它们之前,您应该始终将 Win32 API 结构清零。对于 AF_INET
,您没有用零填充 sockaddr_in::sin_zero
字段,因此它将包含来自调用堆栈的随机字节。这通常没问题,因为该字段通常会被忽略。但是对于 AF_INET6
,您没有填充 sockaddr_in6::sin6_flowinfo
和 sockaddr_in6::sin6_scope_id
字段,它们确实有意义,并且当它们包含随机值时可能是 WSAEADDRNOTAVAIL
错误的根本原因.
改用这个:
struct sockaddr_in6 ip6addr = {};
struct sockaddr_in ip4addr = {};
或:
struct sockaddr_in6 ip6addr;
ZeroMemory(&ip6addr, sizeof ip6addr);
struct sockaddr_in ip4addr;
ZeroMemory(&ip4addr, sizeof ip4addr);
但是,为 bind()
(和 connect()
)创建正确填充的 sockaddr_in
/sockaddr_in6
结构的更好方法是使用 getaddrinfo()
而不是手动填充它们:
const char szHost = "::1"; // or "127.0.0.1"
const char *szPort = "55339";
addrinfo hint = {};
hint.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
hint.ai_family = AF_INET6; // or AF_INET
hint.ai_socktype = SOCK_STREAM;
hint.ai_protocol = IPPROTO_TCP;
addrinfo *addr = NULL;
res = getaddrinfo(szHost, szPort, &hint, &addr);
if (res != 0)
{
wprintf(L"getaddrinfo function failed with error: %d\n", res);
}
else
{
res = bind(slisten, addr->ai_addr, addr->ai_addrlen);
if (res == SOCKET_ERROR)
{
wprintf(L"bind function failed with error: %d\n", WSAGetLastError());
}
freeaddrinfo(addr);
}