c++ - Bind() Windows Socket 适用于 IPv4,但不适用于 IPv6

标签 c++ winsock2

我正在学习如何使用Winsock。我正在编写一个监听连接的服务器。一切都工作正常,直到我想将整个代码更改为 IPv6。

我已经浏览了与该主题相关的每个问题和网站,但没有适合我的解决方案......:/

背景:Windows,使用 Visual Studio 2019,IPv6 已激活并且工作一切正常(例如,我可以在 IPv6 中 ping 我的手机)。

每次我收到错误 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() = 成功

bind() = 错误 10049

如果我注释掉 IPv6 部分并取消注释 IPv4 部分,并在 slisten = socket() 中将 PF_INET6 更改为 PF_INET,则有没有 bind() 错误。

告诉我您是否需要更多代码或其他内容,但我的意思是,如果程序在该特定函数中抛出错误,那么在此之前的一切都必须正常工作,因为没有错误(?)。

代码的某些部分属于 YouTube 上的 s41b0tproducts。您可以通过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_flowinfosockaddr_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);

但是,创建正确填充的 sockaddr_in 的更好方法/sockaddr_in6 bind() 的结构(和 connect() )是使用 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);
}

关于c++ - Bind() Windows Socket 适用于 IPv4,但不适用于 IPv6,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58205051/

相关文章:

c++ - 有没有一种简单的方法来获取用 C++ 打印的字符数?

c++ - 使用 winsock 设置主机名?

c++ - 使用重叠 IO 获取发件人的 IP 地址

c++ - 蓝牙服务问题

c++ - 在OpenCL中以编程方式选择最佳GPU的最佳方法是什么?

C++ GetAsyncKeyState 和 GetCursorPos 与 Windows 消息传递

c++ - 头文件没有看到其他头标识符

php - Winsockets - 主体之前和/或之后的奇怪字符

c - 没有完成键的 IOCP 通知

c++ - 如何避免重复包含头文件