如何存储IP地址列表,在C#列表,使其可搜索的子网吗?(How to store IP addres

2019-08-04 02:05发布

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

有两个例子:

  1. 我有IP地址1.2.3.4,在我的C#列出有1.2.3.4条目,以便我们在这里有没有问题。

  2. 我有IP地址3.4.5.6和在我的C#名单上有子网3.4.0.0/24。 这是我的问题。

如何存储IP子网列表覆盖第二个例子?

谢谢

Answer 1:

在这个答案的末尾,你会发现一个完整实现的结构来表示一个IPv4地址。

下面是关于使用非常简单的例子: -

List<IPV4Address> list = new List<IPV4Address>();
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.6在3.4.0.0/24子网中发现的价值“3.4.0.0/255.255.255.0”显示在矿井控制台。 假设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;
  }
}


Answer 2:

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

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

然后覆盖EqualsGetHashCode ,使得仅所述第一PrefixLength的比特IPAddress.GetAddressBytes()考虑在内(而且,当然,所述的IPAddress类型)。

然后,可以使用此类子网前缀存储在一个List<T>或使用它们作为一个的键Dictionary<K,V>

var subnets = new List<IPAddressWithPrefixLength>
{
    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地址了。



Answer 3:

我希望创建一个专门的结构(类)来存储所有这些信息一起。 也许在不久的将来,你想扩展它来存储命令ipv6 IPv4的旁边,也许更多的数据(公制,网关等)。



Answer 4:

我可以用一个二叉树节点上的布尔标签。 使用标准符号,其中0为左孩子和1右子,1.2.3.4会通过把存储true00000001000000100000001100000100树(该地址的二进制表示) -和错误的根源,这之间的所有节点。 相反地,3.4.0.0/16将存储与true0000001100000100 (第一个16个的3.4.0.0的二进制表示的比特)。

当您将得到测试的地址,只要下井树根据该地址的位:如果你到达一个节点的true ,地址在列表中。 如果你达到一个分支的末端,该地址不在列表中。

举例来说,如果仰视3.4.123.48,你会去倒在树16个水平达到真之前,这意味着该地址在列表中。 但是仰视129.199.195.13,你会知道从该地址,它不是列表的一部分第1位。

我不知道它是多么的重要,你用List类型来存储这些地址,因此这可能没有帮助; OTOH,一旦你已经实现了与标签基本二叉树,这应该比净更好的渐近性能特性List



Answer 5:

不要将其存储在一个列表 - 在结构中其存储如字典来代替,其中关键的是IP地址,并且该值是子网地址。



文章来源: How to store IP address list in C# List to make it searchable for subnets too?