无法在Windows 10上加入IPV6多播组

时间:2018-07-06 14:42:59

标签: c++ networking udp multicast

下面的代码无法加入IP多播组地址0:0:0:0:0:ffff:efc0:202,这是转换为IPv6的IPv4地址239.192.2.2。

失败发生在这里:

 if (setsockopt(m_socket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char*)&multicastRequest, sizeof(multicastRequest)) < 0)
 {
        int rc = WSAGetLastError();

        printf("Failed to join multicast group. Error = %d\n", rc);
 }

WSAGetLastError()返回10049(“所请求的地址在其上下文中无效”),由于双模套接字应该能够从IPv4和IPv6地址接收UDP数据,所以我觉得很奇怪。

如果我尝试这样做:

if (setsockopt(m_socket, IPPROTO_IPV4, IP_ADD_MEMBERSHIP, (char*)&multicastRequest, sizeof(multicastRequest)) < 0)
    {
        int rc = WSAGetLastError();

        printf("Failed to join multicast group. Error = %d\n", rc);
    }

它失败,错误代码rc = 10022(“为setsockopt提供了无效的参数”)

该代码已在Windows 10计算机上的MS Visual Studio 2015中执行。

#define WIN32_LEAN_AND_MEAN

#include <string>
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib")


void main(void)
{
    std::string ip = "0:0:0:0:0:ffff:efc0:202";
    WSADATA wsaData;
    struct ipv6_mreq multicastRequest;
    SOCKET m_socket;
    int off = 0;

    struct addrinfo* tmp = nullptr;
    struct addrinfo   hints = { 0 };

    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);

    if (iResult != 0) {
        printf("WSAStartup failed with error: %d\n", iResult);
        return;
    }

    hints.ai_family = AF_INET6;
    hints.ai_flags = AI_NUMERICHOST;
    getaddrinfo(ip.c_str(), NULL, &hints, &tmp);


    m_socket = socket(AF_INET6, SOCK_DGRAM, 0);

    if (setsockopt(m_socket, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&off, sizeof(off)) < 0)
    {
        int rc = WSAGetLastError();

        printf("Failed to set socket option IPV6_V6ONLY. Error = %d\n", rc);
    }

    memcpy(&multicastRequest.ipv6mr_multiaddr, &((struct sockaddr_in6*)(tmp->ai_addr))->sin6_addr, sizeof(multicastRequest.ipv6mr_multiaddr));

    multicastRequest.ipv6mr_interface = 0;

    if (setsockopt(m_socket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char*)&multicastRequest, sizeof(multicastRequest)) < 0)
    {
        int rc = WSAGetLastError();

        printf("Failed to join multicast group. Error = %d\n", rc);
    }

   if (setsockopt(m_socket, IPPROTO_IPV4, IP_ADD_MEMBERSHIP (char*)&multicastRequest, sizeof(multicastRequest)) < 0)
    {
       int rc = WSAGetLastError();

       printf("Failed to join multicast group. Error = %d\n", rc);
    }
}

1 个答案:

答案 0 :(得分:0)

我相当确定您的问题是由尝试向IPv6组播组添加IPv4地址(尽管编码为映射到IPv4的IPv6地址)引起的。 尝试以这种方式使用IPV6_ADD_MEMBERSHIP会在Linux上导致EINVAL错误。

相反,请尝试使用适当的setsockopt()调用将其添加到IPv4组播组(即使用IP_ADD_MEMBERSHIP),而使用包含本地IPv4地址而不是{{ 1}}包含IPv4映射的地址,例如

struct in_addr

使用基于您的代码(请参见https://pastebin.com/NBZ4KNYf),我已经成功创建了一个双堆栈套接字,并将其分别附加到IPv6组播组和IPv4套接字上,尽管在Linux而不是Windows上。

我能够加入两个dns-sd(RFC 6763)多播组并通过IPv4和IPv6接收数据包。在这两种情况下,struct in6_addr调用都给我返回了一个memset(&hints, 0, sizeof hints); hints.ai_family = AF_INET; hints.ai_flags = AI_NUMERICHOST; getaddrinfo("224.0.0.251", NULL, &hints, &tmp); struct sockaddr_in *in4 = (struct sockaddr_in*)tmp->ai_addr; struct in_addr addr4 = in4->sin_addr; struct ip_mreqn mreq4 = { 0 }; memcpy(&mreq4.imr_multiaddr, &addr4, sizeof addr4); mreq4.imr_ifindex = 0; if (setsockopt(m_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq4, sizeof(mreq4)) < 0) { perror("setsockopt(IP_ADD_MEMBERSHIP)"); return -1; } 格式的地址。