如何将套接字绑定到ipv6地址?

时间:2019-10-23 21:15:11

标签: c++ server bind ipv6

我正在尝试将ipv4应用程序移植到ipv6,但是我无法将套接字绑定到ipv6地址。

问题在这里:

err=bind(listening, (sockaddr*)&hint, sizeof(hint));

err应该为0,但是在此代码中它返回-1。怎么了?

SOCKET listening = socket(AF_INET6, SOCK_STREAM, 0);
    if (listening == INVALID_SOCKET)
    {
        cerr << "Can't create a socket! Quitting" << endl;
        return;
    }
    int err;
    // Bind the ip address and port to a socket
    sockaddr_in6 hint;
    hint.sin6_family = AF_INET6;
    hint.sin6_flowinfo = 0;
    hint.sin6_port = htons(54000);
    hint.sin6_addr = in6addr_any;
    err=bind(listening, (sockaddr*)&hint, sizeof(hint)); //<======= here

2 个答案:

答案 0 :(得分:4)

您可以(并且应该)改为使用getaddrinfo(),而不是手动填充sockaddr_in6,并让它为您分配正确填充的sockaddr_in6,例如:

int err;

SOCKET listening = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (listening == INVALID_SOCKET)
{
    err = WSAGetLastError(); // or errno on non-Windows platforms...
    cerr << "Can't create a socket! Error " << err << ". Quitting" << endl;
    return;
}

// Bind the ip address and port to a socket

addrinfo hint = {};
hint.ai_flags = AI_NUMERICHOST | AI_PASSIVE;
hint.ai_family = AF_INET6;
hint.ai_socktype = SOCK_STREAM;
hint.ai_protocol = IPPROTO_TCP;

addrinfo *res;

err = getaddrinfo("::0", "54000", &hint, &res);
if (err != 0)
{
    cerr << "Can't get address to bind the socket! Error " << err << ". Quitting" << endl;
    closesocket(listening); // or close() on non-Windows platforms...
    return;
}

err = bind(listening, res->ai_addr, res->ai_addrlen);
if (err == SOCKET_ERROR)
{
    err = WSAGetLastError(); // or errno on non-Windows platforms...
    cerr << "Can't bind the socket! Error " << err << ". Quitting" << endl;
    freeaddrinfo(res);
    closesocket(listening); // or close() on non-Windows platforms...
    return;
}

freeaddrinfo(res);

...

答案 1 :(得分:2)

这可能取决于您的平台,但是在Linux上,从2.4开始,sockaddr_in6结构还包含一个sin6_scope_id成员,用于定义IPv6范围,并且由于变量hint在堆栈中,其中包含随机数据。

IPv6范围描述了它是一种什么样的地址:单播,多播,本地链接和其他一些地址,而我对它们只有一个了解。但是,如果那里有垃圾,那可能是一回事。

建议通过将sin6_scope_id硬设置为零,或者(最好)在将整个sockaddr_in6分配给对象之前(最好)将其归零来将其排除为问题;长期以来,我一直使用sockaddr_in变量来做到这一点,只是为了确保我不会因为不想过而成为垃圾。

memset(&hint, 0, sizeof hint);

是的,errno非常重要。