在 IPv4 中,[IGMP 版本 3 增加了对“源过滤”的支持,即系统能够报告对接收数据包的兴趣*仅*来自特定源地址。][1]
我在 C# 应用程序中使用 IGMPv3 来支持这种行为。这是我的做法。
我现在正在我的应用程序中添加对 IPv6 的支持,我需要获得与 IPv4 相同的行为。根据我的阅读,IPv6 中 IGMPv3 的等效协议是MLDv2。有人知道如何在 C# 中使用 Socket 实现这一点吗?
谢谢!
RFC3678协议独立 API 仅在 Vista+ 中可用,这可能解释了该问题。
如果 C# 运行时完全支持 IPv6,您将不得不尝试匹配GROUP_REQ或GROUP_SOURCE_REQ结构。没有针对 SSM 的 IPv6 特定 API 与 IPv4 API 相匹配,因为开发人员最终放弃了 API 的无用重复,并最终确定了一个超级集。
不幸的是,C# 很可能实现了ipv6_mreqAddMembership
并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
为了跟进 Steve-o 的答案,即使 System.Net.Sockets.SocketOptionName 枚举没有通过直接转换数字来定义所需的选项,仍然可以在 C# 中在 IPv6 中进行源过滤。
(SocketOptionName) 45; //MCAST_JOIN_SOURCE_GROUP
套接字的函数 SetSocketOption 将让调用转到“windows 套接字”,即使该选项未被识别。真正的斗争变成了需要与选项一起发送的数据结构本身。要设置源过滤,数据结构必须是这样的:group_source_req。前一个结构使用sockaddr_storage,它通常位于与sockaddr_in和sockaddr_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];
}
现在可以使用此处提供的解决方案提取 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*/);