【问题标题】:Failure to join IPV6 multicast group on Windows 10在 Windows 10 上加入 IPV6 多播组失败
【发布时间】:2018-07-06 14:42:58
【问题描述】:

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

故障发生在这里:

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

【问题讨论】:

  • 你是什么意思'失败与 10049'?如果发生错误,Setsockopt 返回 -1。是 ERRNO 值还是什么?
  • @SergeyA 对不起。是的,返回 10049 的是 WSAGetLastError()
  • 您是否费心从 WinSock 中查找错误代码 10049 的实际含义?
  • 因为您没有发布 MCVE(没有显示调用 WSAGetLastError 的代码),并且您没有在 Microsoft 文档网站上查看此错误代码的含义。
  • FWIW,我从未尝试过,但如果将 IPv4 映射的 IPv6 地址添加到 IPv6 多播组中甚至是合法的/可能的,我会感到非常惊讶。 IPv6 有自己特定的多播范围 (ff00::/8)

标签: c++ networking udp multicast


【解决方案1】:

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

尝试使用适当的 setsockopt() 调用将其添加到 IPv4 多播组(即使用 IP_ADD_MEMBERSHIP),使用包含本机 IPv4 地址的普通 struct in_addr 而不是包含 IPv4 映射的 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;
}

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

我能够加入两个 dns-sd (RFC 6763) 多播组并通过 IPv4 和 IPv6 接收数据包。在这两种情况下,recvfrom 调用都给我返回了AF_INET6 格式的地址。

【讨论】:

  • 您可能还必须传递等效的(真实)IPv4 地址。
  • 好的,因此可以在双模式套接字上从 IPv4 和 IPv6 地址接收 UDP,也可以将 UDP 数据发送到这些地址,但加入多播组以从无法将 IP 指定为从 IPv4 转换为 IPv6 的地址的 IPv4 地址。这其中的逻辑在哪里?
  • 您当然也不能将 ipv6_mreq 传递给 IPv4 多播组 setsockopt() 调用。
  • 正确,但没关系。无论如何,我都会遇到同样的错误。
  • IPv6 多播有很多 IPv4 多播根本没有的东西。 IPv6 多播必须使用一些标志和范围,弄错这些可能会搞砸。无论如何,IPv6 多播必须在ff00::/8 范围内,而您转换后的 IPv4 地址不在该范围内。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多