如何在C#List中存储IP地址列表,使其也可以搜索到子网?

我应该如何正确地存储IP地址列表和地址,使其成为可搜索的子网?

有两个例子:

  1. 我有IP地址1.2.3.4,在我的C#List中有1.2.3.4条目,所以这里我们没有问题。

  2. 我有IP地址3.4.5.6,在我的C#列表中我有子网3.4.0.0/24。 这是我的问题。

如何在List中存储IP子网以涵盖第二个示例?

谢谢

在本答案的最后,您将找到一个表示IPV4地址的结构的完整实现。

这是一个非常简单的用法示例: –

List list = new List(); list.Add(IPV4Address.FromString("3.4.0.0", 24)); var x = IPV4Address.FromString("3.4.0.6"); foreach (var addr in list.Where(a => a.Contains(x))) Console.WriteLine(addr); 

在控制台中显示值“3.4.0.0/255.255.255.0”,因为在3.4.0.0/24子网中找到了3.4.0.6。 假设list中有各种子网, x可以包含任何地址,那么: –

 var result = list.Where(a => a.Contains(x)) .OrderByDescending(a => a.Mask) .FirstOrDefault(); 

将为包含x的最具体的子网选择。

 public struct IPV4Address { private UInt32 _Value; private UInt32 _Mask; public UInt32 Value { get { return _Value; } private set { _Value = value; } } public UInt32 Mask { get { return _Mask; } private set { _Mask = value; } } public static IPV4Address FromString(string address) { return FromString(address, 32); } public static IPV4Address FromString(string address, int maskLength) { string[] parts = address.Split('.'); UInt32 value = ((UInt32.Parse(parts[0]) << 24) + ((UInt32.Parse(parts[1])) << 16) + ((UInt32.Parse(parts[2])) << 8) + UInt32.Parse(parts[3])); return new IPV4Address(value, maskLength); } public IPV4Address(UInt32 value) { _Value = value; _Mask = int.MaxValue; } public IPV4Address(UInt32 value, int maskLength) { if (maskLength < 0 || maskLength > 32) throw new ArgumentOutOfRangeException("maskLength", "Must be 0 to 32"); _Value = value; if (maskLength == 32) _Mask = UInt32.MaxValue; else _Mask = ~(UInt32)((1 << (32 - maskLength))-1); if ((_Value & _Mask) != _Value) throw new ArgumentException("Address value must be contained in mask"); } public bool Contains(IPV4Address address) { if ((Mask & address.Mask) == Mask) { return (address.Value & Mask) == Value; } return false; } public override string ToString() { string result = String.Format("{0}.{1}.{2}.{3}", (_Value >> 24), (_Value >> 16) & 0xFF, (_Value >> 8) & 0xFF, _Value & 0xFF); if (_Mask != UInt32.MaxValue) result += "/" + String.Format("{0}.{1}.{2}.{3}", (_Mask >> 24), (_Mask >> 16) & 0xFF, (_Mask >> 8) & 0xFF, _Mask & 0xFF); return result; } } 

定义一个存储IPAddress和前缀长度的类:

 public class IPAddressWithPrefixLength { public IPAddress IPAddress { get; } public int PrefixLength { get; } } 

然后重写EqualsGetHashCode ,以便仅考虑IPAddress.GetAddressBytes()的第一个PrefixLengthIPAddress.GetAddressBytes()当然,还考虑IPAddress类型)。

然后,您可以使用此类将子网前缀存储在List或将它们用作Dictionary

 var subnets = new List { new IPAddressWithPrefixLength(IPAddress.Parse("1.2.3.4"), 32), new IPAddressWithPrefixLength(IPAddress.Parse("3.4.0.0"), 16), }; var ipawpl = new IPAddressWithPrefixLength(IPAddress.Parse("3.4.5.6"), 16); Console.WriteLine(subnets.Contains(ipawpl)); // prints "True" 

这也适用于IPv6地址。

我更愿意创建一个专门的结构(类)来将所有这些信息存储在一起。 可能在不久的将来,您希望将其扩展到ipv4旁边的ipv6,以及更多的数据(公制,网关等)。

我可以在节点上使用带有布尔标签的二叉树。 使用标准符号,其中0是左子项,1是右子项,1.2.3.4将通过在树中将00000001000000100000001100000100 (该地址的二进制表示)置为true来存储 – 并且在根和此之间的所有节点处为false 。 相反,3.4.0.0/16将以true值存储在0000001100000100 (二进制表示forms的前16位为3.4.0.0)。

当您获得要测试的地址时,只需根据该地址的位向下移动树:如果您到达节点为true ,则该地址在列表中。 如果到达分支的末尾,则该地址不在列表中。

例如,如果查找3.4.123.48,则在达到true之前,您将在树中下降16个级别,这意味着此地址位于列表中。 但是查看129.199.195.13,你会从该地址的前1位知道它不是列表的一部分。

我不确定使用List类型存储这些地址对你有多重要,所以这可能没有用; OTOH,一旦你用标签实现了一个基本的二叉树,这应该具有比.Net List更好的渐近性能特征。

不要将其存储在列表中 – 将其存储在诸如Dictionary之类的结构中,其中键是IP地址,值是子网地址。