【问题标题】:How to use MLDv2 in C# (IPv6 Multicast)如何在 C# 中使用 MLDv2(IPv6 多播)
【发布时间】:2011-09-08 19:32:03
【问题描述】:

在 IPv4 中,[IGMP 版本 3 增加了对“源过滤”的支持,即系统能够报告对接收数据包的兴趣*仅*来自特定源地址。][1]

我在 C# 应用程序中使用 IGMPv3 来支持这种行为。 Here is how I do it.

我现在正在我的应用程序中添加对 IPv6 的支持,我需要获得与 IPv4 中相同的行为。根据我的阅读,IPv6 中 IGMPv3 的等效协议是MLDv2。有人知道如何在 C# 中使用 Socket 实现这一点吗?

谢谢!

【问题讨论】:

    标签: c# ipv6 multicast igmp


    【解决方案1】:

    RFC3678 与协议无关的 API 仅在 Vista+ 中可用,这或许可以解释问题。

    如果 C# 运行时完全支持 IPv6,您将不得不尝试匹配 GROUP_REQGROUP_SOURCE_REQ 结构。没有针对 SSM 的 IPv6 特定 API 与 IPv4 API 相匹配,因为开发人员最终放弃了 API 的愚蠢重复,并最终确定了一个超级集。

    不幸的是,C# 为 AddMembershipAddSourceMembership 实现 ipv6_mreq 可能会失败。文档完全没有详细说明。

    所有需要的 SocketOptionName 值都没有在 C# 中定义:

    /* RFC 3678 */
    #define MCAST_JOIN_GROUP       41
    #define MCAST_LEAVE_GROUP      42
    #define MCAST_BLOCK_SOURCE     43
    #define MCAST_UNBLOCK_SOURCE   44
    #define MCAST_JOIN_SOURCE_GROUP        45
    #define MCAST_LEAVE_SOURCE_GROUP       46
    #define MCAST_MSFILTER         47
    

    【讨论】:

    • 感谢您的回答。对于没有特定源的 IPv6 多播,我能够匹配 GROUP_REQ 结构并在设置套接字选项时使用 AddMembership(另一个选项是使用 IPv6MulticastOption),但对 GROUP_SOURCE_REQ 和 AddSourceMembership 做同样的事情只是继续抛出异常。
    【解决方案2】:

    为了跟进 Steve-o 的回答,即使 System.Net.Sockets.SocketOptionName 枚举没有通过直接转换数字来定义所需的选项,仍然可以在 C# 中的 IPv6 中进行源过滤。

    (SocketOptionName) 45; //MCAST_JOIN_SOURCE_GROUP
    

    即使选项不被识别,套接字的函数SetSocketOption也会让调用转到“windows套接字”。真正的斗争变成了需要与选项一起发送的数据结构本身。 要设置源过滤,数据结构必须是这样的:group_source_req。前一个结构使用sockaddr_storage,它通常位于sockaddr_insockaddr_in6 的联合中。为了复制这种行为,我们可以像这样定义相同的结构:

    private unsafe struct sockaddr_storage
    {
        public short ss_family;             //2
        private fixed byte __ss_pad1[6];    //6
        private Int64 __ss_align;           //8
        private fixed byte __ss_pad2[112];  //112
    }
    private unsafe struct sockaddr_in
    {
        public ushort sin_family;       //2
        public ushort sin_port;         //2
        public fixed byte sin_addr[4];  //4
        private fixed byte sub_zero[8]; //8
     }
    private unsafe struct sockaddr_in6
    {
        public ushort sin6_family;       //2
        public ushort sin6_port;         //2
        public int sin6_flowinfo;        //4
        public fixed byte sin6_addr[16]; //16
        public uint sin6_scope_id;       //4
    }
    private struct group_source_req
    {
        public uint gr_interface;           //4
        //Compiler add a padding here:      //4
        public sockaddr_storage gr_group;   //128
        public sockaddr_storage gr_source;  //128
    }
    

    您现在可以通过执行以下操作创建一个 sockaddr_in6:

    sockaddr_in6 sockIn = new sockaddr_in6
    {
        sin6_family = (ushort) endPoint.AddressFamily,
        sin6_port = (ushort)endPoint.Port,
        sin6_scope_id = 0
    };
    for (int i = 0; i < endPoint.Address.GetAddressBytes().Length; i++)
    {
        sockIn.sin6_addr[i] = endPoint.Address.GetAddressBytes()[i];
    }
    

    现在可以使用here 提供的解决方案提取 sockaddr_in6 的字节,并直接复制到之前创建的 sockaddr_storage 中:

    sockaddr_storage sock = new sockaddr_storage
    {
        ss_family = (short)endPoint.AddressFamily
    };
    //[...]
    byte[] sockInData = getBytes(sockIn);
    byte* sockData = (byte*) &sock;
    for (int i = 0; i < sockInData.Length; i++)
    {
        sockData [i] = sockInData[i];
    }
    

    现在您有了一个 sockaddr_storage,您可以将它分配给 group_source_req 并像我们之前所做的那样提取 group_source_req 的数据,并在您设置选项时将其用作值。

    socket.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName) 45, /*data extracted from group_source_req*/);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-12-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-26
      • 1970-01-01
      • 2015-04-16
      • 2011-04-20
      相关资源
      最近更新 更多