如何在C#中使用MLDv2(IPv6组播)

时间:2011-09-08 19:32:04

标签: c# ipv6 multicast igmp


在IPv4中,Version 3 of IGMP adds support for "source filtering", that is, the ability for a system to report interest in receiving packets only from specific source addresses.

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

我现在正在申请中添加对IPv6的支持,我需要获得与IPv4相同的行为。根据我的阅读,IPv6中与IGMPv3等效的协议是MLDv2。有人知道如何用Cocket实现这个吗?

谢谢!

2 个答案:

答案 0 :(得分:1)

RFC3678协议独立API仅适用于Vista +,可解释此问题。

如果C#运行时完全支持IPv6,则必须尝试匹配GROUP_REQGROUP_SOURCE_REQ结构。 SSM没有与IPv6 API匹配的IPv6特定API,因为开发人员最终放弃了API的无聊复制,最终确定了一个超级集。

很遗憾,C#为AddMembership实现ipv6_mreq并且AddSourceMembership失败。文档完全没有详细说明。

所需的所有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

答案 1 :(得分:0)

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

(SocketOptionName) 45; //MCAST_JOIN_SOURCE_GROUP

套接字的函数SetSocketOption将让调用转到" windows socket"即使该选项未被识别。真正的斗争变成了数据结构本身,需要与选项一起发送。 要设置源过滤,数据结构必须与此类似: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*/);