IP 列表,在 C# 中对照列表检查 IP 号码





5.00/5 (16投票s)
一个用于存储 IP 号码列表的类,你可以用新的 IP 号码对照此列表进行检查。
引言
您是否想要构建一个具有基于 IP 号码的简单访问控制功能的 Internet 应用程序,或者出于其他原因需要根据列表验证 IP 号码?如果是这样,您就找到了适合的类。
使用该类
该类应该易于使用,创建一个 IPList
的实例,Add
一个或多个 IP 号码,然后开始对照列表检查 IP 号码,如下所示:
IPList iplist = new IPList();
iplist.Add("10.0.0.7");
if (iplist.CheckNumber("10.0.0.7"))
System.Console.WriteLine("10.0.0.7 found.");
就是这样……哦,您还可以添加 IP 号码的范围,但基本上这就是使用该类所要做的全部工作。
可用方法
IPList
类具有以下可用方法:
添加 IP 号码和范围
添加单个 IP 号码(字符串格式 "10.1.1.1")或无符号整数(0x0A010101)。IP 号码实际上是使用默认掩码 255.255.255.255(/32 级别)添加的,这意味着所有 32 位都是固定的,因此我们有一个“范围”为单个号码。
void Add(string ipNumber)
void Add(uint ip)
使用 IP 掩码添加一个或多个 IP 号码,例如 "10.1.1.1"、"255.255.0.0",这将添加从 10.1.0.0 到 10.1.255.255 的范围。
void Add(string ipNumber, string mask)
void Add(uint ip, uint umask)
使用级别添加一个或多个 IP 号码,例如 192.168.1.0/24,这将添加 192.168.1.0 到 192.168.1.255。该方法使用级别来查找正确的 IP 掩码并调用上面的添加方法。
void Add(string ipNumber, int maskLevel)
添加一个由“起始 IP”和“结束 IP”定义的 IP 号码范围。该方法将范围分解为标准的 IP 范围并找到它们的掩码。因此,“10.0.0.5”到“10.0.0.20”的范围将被分解为以下范围并添加到列表中:10.0.0.5、10.0.0.20、10.0.0.6/31、10.0.0.16/30 和 10.0.0.8/29。
void AddRange(string fromIP, string toIP)
void AddRange(uint fromIP, uint toIP)
检查 IP 号码
CheckNumber
方法简单地检查一个 IP 地址是否在添加到类中的任何 IP 范围之内,返回 true
或 false
。
bool CheckNumber(string ipNumber)
bool CheckNumber(uint ip)
左键单击 gadget 并拖动以移动它。左键单击 gadget 的右下角并拖动以调整其大小。右键单击 gadget 以访问其属性。
IPList
在构造函数中不接受任何参数,并重写了 ToString
方法以提供一个可打印的列表,列出列表中的所有 IP 范围。Clear
方法则简单地清除类中的所有 IP 范围。
IPList()
void Clear()
string ToString()
类实现
现在您可以使用 IPList
类了。但如果您对它是如何制作的感兴趣,请继续阅读,我将尝试解释内部机制和类的设计。
该类实际上是两个类,一个公开可用,一个仅供内部使用。
IPArrayList
IPList
有一个内部类称为 IPArrayList
,它保存一个具有相同 IP 掩码的 IP 号码列表。IPArrayList
使用 ArrayList
来保存 IP 号码,这为我们提供了排序和二分查找功能。该类还持有一个脏标志,因此列表仅在有人弄乱了我们的列表时才会被排序。
internal class IPArrayList {
private bool isSorted = false;
private ArrayList ipNumList = new ArrayList();
private uint ipmask;
public IPArrayList(uint mask) ;
public void Add(uint IPNum) ;
public bool Check(uint IPNum);
public void Clear();
public override string ToString();
public uint Mask { get { return ipmask; } }
}
该类包含两个重要方法,一个用于将 IP 号码添加到列表,另一个使用二分查找来检查给定的 IP 号码。
IP 掩码在构造函数中设置,该类具有一些管理方法,用于清除列表和获取所有包含的 IP 号码的字符串列表。
IPList
IPList
包含一个名为 ipRangeList
的 ArrayList
,其中包含 32 个 IPArrayList
对象 - 每个对象对应一个可能的 IP 掩码。
usedList
将保存我们实际上输入了范围的 IPArrayList
的数量,因此我们只需检查这些活动的 IPArrayList
。
最后一个列表是已排序的列表,其中包含所有可能的掩码及其级别编号。此列表用于在级别编号和 IP 掩码之间进行转换。
这是 IPList
类的完整接口
public class IPList
{
private ArrayList ipRangeList = new ArrayList();
private SortedList maskList = new SortedList();
private ArrayList usedList = new ArrayList();
public IPList();
private uint parseIP(string IPNumber);
public void Add(string ipNumber);
public void Add(uint ip);
public void Add(string ipNumber, string mask);
public void Add(uint ip, uint umask);
public void Add(string ipNumber, int maskLevel);
public void AddRange(string fromIP, string toIP);
public void AddRange(uint fromIP, uint toIP);
public bool CheckNumber(string ipNumber);
public bool CheckNumber(uint ip);
public void Clear();
public override string ToString();
}
构造函数
构造函数生成 32 个掩码,将它们添加到 maskList
,并实例化 32 个 IPArrayList
对象,然后将它们添加到 ipRangeList
。
public IPList()
{
// Initialize IP mask list and create
// IPArrayList into the ipRangeList
uint mask = 0x00000000;
for (int level = 1; level<33; level++)
{
mask = (mask >> 1) | 0x80000000;
maskList.Add(mask,level);
ipRangeList.Add(new IPArrayList(mask));
}
}
IP 字符串解析器
该类有一个简单的、不进行验证的将字符串地址转换为 32 位无符号整数的解析器。我们不能使用 System.Net.IPAddress
中的解析器,因为它包含过多的验证,所以它不会正确解析我们的掩码,例如 255.255.0.0 会被解析为 65535。
待办事项:如果 IP 地址不包含四个元素、三个句点,并且所有元素都是介于 0 和 255 之间的整数,则在此处抛出一些异常会很好。
private uint parseIP(string IPNumber)
{
uint res = 0;
string[] elements = IPNumber.Split(new Char[] {'.'});
if (elements.Length==4) {
res = (uint) Convert.ToInt32(elements[0])<<24;
res += (uint) Convert.ToInt32(elements[1])<<16;
res += (uint) Convert.ToInt32(elements[2])<<8;
res += (uint) Convert.ToInt32(elements[3]);
}
return res;
}
使用字符串解析器
字符串解析器用于我们所有重载的方法,以快速将字符串转换为无符号整数,并调用采用此参数的重载方法。
这是一个 Add
方法的示例
public void Add(string ipNumber, string mask)
{
this.Add(parseIP(ipNumber),parseIP(mask));
}
添加到列表
添加到列表时,我们需要掩码来将 IP 号码修剪到最近的范围开始编号,以及掩码级别以将 IP 号码添加到我们 ipRangeList
中的正确列表。我们还更新 usedList
并在必要时对其进行排序。
掩码级别减一,因为我们的 ipRangeList
的索引值为 0 到 31,而我们的级别为 1 到 32。
public void Add(uint ip, uint umask) {
object Level = maskList[umask];
if (Level!=null) {
ip = ip & umask;
((IPArrayList)ipRangeList[(int)Level-1]).Add(ip);
if (!usedList.Contains((int)Level-1)) {
usedList.Add((int)Level-1);
usedList.Sort();
}
} // else throw exception !
}
检查列表
在搜索 IP 号码时,我们确保一旦找到匹配项就立即退出检查。此外,我们仅搜索包含活动范围的列表,并且通过对 usedList
进行排序,我们确保所有搜索都从最大的范围开始,最后搜索单个 IP 号码。
public bool CheckNumber(uint ip) {
bool found = false;
int i=0;
while (!found && i<usedList.Count) {
found =
((IPArrayList)ipRangeList[(int)usedList[i]]).Check(ip);
i++;
}
return found;
}
清空列表
要清空列表,我们遍历所有使用的列表并调用列表的 clear 方法。最后,我们清空 usedList。
public void Clear()
{
foreach (int i in usedList)
{
((IPArrayList)ipRangeList[i]).Clear();
}
usedList.Clear();
}
获取包含的所有 IP 范围的可打印列表
该类重写了标准的 ToString
方法,以提供一种简单的方式来获取包含的所有 IP 号码的可打印列表。这是调试时的有效工具。
与 clear 方法一样,ToString
方法会遍历所有使用的列表,并将它们的列表添加到字符串生成器中,然后将连接的结果返回给调用者。
public override string ToString() {
StringBuilder buffer = new StringBuilder();
foreach (int i in usedList) {
buffer.Append(
"\r\nRange with mask of ").Append(i+1).Append("\r\n");
buffer.Append(((IPArrayList)ipRangeList[i]).ToString());
}
return buffer.ToString();
}
将任意 IP 号码范围分解为标准的 IP/掩码范围
好的,我们把最好的留到最后。将 IP 号码范围分解为标准范围。
public void AddRange(uint fromIP, uint toIP) {
// If the order is not asending,
// switch the IP numbers.
if (fromIP>toIP) {
uint tempIP = fromIP;
fromIP=toIP;
toIP=tempIP;
}
//First we check if the range is ascending,
//else we switch the numbers.
if (fromIP==toIP) {
this.Add(fromIP);
//If it is a single IP number, just add it.
} else {
uint diff = toIP-fromIP;
int diffLevel = 1;
uint range = 0x80000000;
if (diff<256) {
diffLevel = 24;
range = 0x00000100;
}
while (range>diff) {
range = range>>1;
diffLevel++;
}
//Then we find the largest standard range that will fit
// into the supplied range. As most ranges are small
// we make a shortcut for these.
uint mask =
(uint)maskList.GetKey(maskList.IndexOfValue(diffLevel));
uint minIP = fromIP & mask;
if (minIP<fromIP) minIP+=range;
//Then we find the mask for the standard range and
//ensures that the standard
//range starts within
//our supplied range.
if (minIP>fromIP) {
this.AddRange(fromIP,minIP-1);
fromIP=minIP;
}
//If there are IP numbers between the start
//of the supplied range and the start of
//the standard range, add them recursively.
if (fromIP==toIP) {
this.Add(fromIP);
//If the new range is of one IP number,
//then just add it.
} else {
if ((minIP+(range-1))<=toIP) {
this.Add(minIP,mask);
fromIP = minIP+range;
//If the standard range fits within the
//supplied range, add the standard
//range with the correct mask.
}
if (fromIP==toIP) {
this.Add(toIP);
//If the new range is of one IP number,
//then just add it.
} else {
if (fromIP<toIP) this.AddRange(fromIP,toIP);
//Add the rest range recursively.
}
}
}
}
源代码信息
源文件中包含两个目录,ipnumbers,包含此处描述的类,以及 ipnumberstest,包含一个使用该类的简单控制台应用程序。ipnumbers 目录中的 Visual Studio 解决方案文件包含这两个项目。
待办事项信息
该类仅使用简单的数据集进行测试,并且该类缺少对提供的 IP 号码和 IP 掩码的验证。在生产环境中使用之前,应修复这些问题。
该类应扩展功能,可以将其存储在列表中的所有信息序列化和反序列化到存储格式,例如 XML 或类似格式。
Copyright
该代码版权归 Bo Norgaard 所有,但使用代码是免费的,无需许可证,也不收取任何版税。