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

时间:2019-10-02 15:51:07

标签: 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部分,并将PF_INET6中的PF_INET更改为slisten = socket(),则没有bind()错误。

如果需要更多代码或其他内容,请告诉我,但是我的意思是,如果程序在该特定功能上引发错误,那么在此之前,所有操作都必须工作正常,因为没有错误(?)。

该代码的某些部分属于YouTube上的s41b0t作品。您可以在https://www.youtube.com/user/s41b0tproductions/videos找到他。

1 个答案:

答案 0 :(得分:0)

呼叫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);
}