zoukankan      html  css  js  c++  java
  • IP地址与UInt之间不得不说的故事

    IP地址有很多种表示方式,比如原始的二进制,常用的点分十进制。但在涉及到一些有关IP的计算时,这两种表示方式的操作都不是很方便。本文介绍最近在网管项目中使用的uint型ip表示法。使用这种方式,不论是在IP地址的存储,还是计算处理上,都能得到立竿见影的效果。

    首先贴出将IP转型为uint的代码:

     1     /// <summary>
     2     /// 将点分十进制格式的IP转换为UInt格式
     3     /// </summary>
     4     /// <param name="ipStr">IP的点分十进制表示</param>
     5     /// <returns></returns>
     6     private uint GetUIntFromIP(string ipStr)
     7     {
     8         if (String.IsNullOrEmpty(ipStr) || !IsIPAddress(ipStr)) return 0;
     9         string[] ipArr = ipStr.Split('.');
    10         List<byte> ipIntArr = new List<byte>();
    11         foreach (var s in ipArr)
    12         {
    13             ipIntArr.Add(byte.Parse(s));
    14         }
    15         uint result = 0;
    16         for (int i = 0; i < 4; i++)
    17         {
    18             result += (uint)(ipIntArr[i] << (24 - i * 8));
    19         }
    20         return result;
    21     }
    22     /// <summary>
    23     /// 验证是否是正确的IP地址
    24     /// </summary>
    25     /// <param name="ipStr">IP字符串</param>
    26     /// <returns></returns>
    27     private bool IsIPAddress(string IpStr)
    28     {
    29         if (String.IsNullOrEmpty(IpStr)) return false;
    30         Regex regText = new Regex(@"^((1?\d?\d|(2([0-4]\d|5[0-5])))\.){3}(1?\d?\d|(2([0-4]\d|5[0-5])))$");
    31         return regText.IsMatch(IpStr);
    32     }

    在上面的GetLongFromIP方法中,首先进行了简单的IP验证,然后将IP地址拆分成byte型的4段。循环中的关键代码 result += (ipUintArr[i] << (24 - i * 8)) 对IP地址的4段分别进行移位操作,再将结果相加。

    我们知道,IP地址的最大值表示为:255.255.255.255,也就是其二进制表示的32位全为1。该最大值用上述方法处理后的结果为:4294967295。这正好是C#中uint值的最大值(C#中uint为32位无符号整数)。可以看到,用这种方式将IP地址转换为uint是再合适不过。

    理解了上述代码,再将uint格式的IP转换为常见的点分十进制格式,就很简单了,基本上就是上述方法的逆运算:

     1     /// <summary>
     2     /// 将UInt格式的IP转换为点分十进制格式
     3     /// </summary>
     4     /// <param name="ip"></param>
     5     /// <returns></returns>
     6     private string GetIPStrFromUint(uint ip)
     7     {
     8         byte first = (byte)(ip >> 24);
     9         byte second = (byte)((ip - (first << 24)) >> 16);
    10         byte third = (byte)((ip - (second << 16) - (first << 24)) >> 8);
    11         byte four = (byte)(ip - (second << 16) - (first << 24) - (third << 8));
    12         return String.Format("{0}.{1}.{2}.{3}", first, second, third, four);
    13     }

    掌握了IP地址与UInt格式的互转,会给我们在IP地址的处理上带来很多方便。比如常见的某一个网段之间的IP数量(这里的网段用开始IP和结束IP标识):

     1     /// <summary>
     2     /// 获取网段之间的IP数量
     3     /// </summary>
     4     /// <param name="beginIP"></param>
     5     /// <param name="endIP"></param>
     6     /// <returns></returns>
     7     private uint GetIPCountFromNet(string beginIP,string endIP)
     8     {
     9         if (String.IsNullOrEmpty(beginIP) || String.IsNullOrEmpty(endIP)) return 0;
    10         return GetUIntFromIP(endIP) - GetUIntFromIP(beginIP) + 1;
    11     }

    再比如获取两个IP之间包含的IP地址列表:

     1     /// <summary>
     2     /// 获取两个IP之间的IP列表
     3     /// </summary>
     4     /// <param name="beginIP"></param>
     5     /// <param name="lastIP"></param>
     6     /// <returns></returns>
     7     private IEnumerable<string> GetIPListFromNet(string beginIP, string lastIP)
     8     {
     9         if (String.IsNullOrEmpty(beginIP) || String.IsNullOrEmpty(lastIP)) yield return null;
    10 
    11         uint beginAddress = GetUIntFromIP(beginIP);
    12         uint lastAddress = GetUIntFromIP(lastIP);
    13         uint temp = lastAddress - beginAddress;
    14         for (uint i = 0; i <= temp; i++)
    15         {
    16             uint tempAddress = beginAddress + i;
    17             //将UInt格式的IP转换为点分十进制表示
    18             string ipAddress =  GetIPStrFromUint(tempAddress);
    19             yield return ipAddress;
    20         }
    21     }

    另外,如果要查询一个IP地址是否属于某个网段,转换成UInt格式以后非常方便。而传统的Sql不会智能到可以判断 begin_ip<='xxx.xxx.xxx.xxx'<=end_ip。当然,也可以利用一些Sql内置函数实现此功能,只是相比本文提到的UInt方式,相对繁琐:

    select count(id) 
    from ip_net 
    where 
        right('000' + parsename('xxx.xxx.xxx.xxx',4),3) 
        + right('000' + parsename('xxx.xxx.xxx.xxx',3),3) 
        + right('000' + parsename('xxx.xxx.xxx.xxx',2),3) 
        + right('000' + parsename('xxx.xxx.xxx.xxx',1),3)
    between 
        right('000' + parsename(begin_ip,4),3) 
        + right('000' + parsename(begin_ip,3),3) 
        + right('000' + parsename(begin_ip,2),3) 
        + right('000' + parsename(begin_ip,1),3)
    and
        right('000' + parsename(end_ip,4),3) 
        + right('000' + parsename(end_ip,3),3) 
        + right('000' + parsename(end_ip,2),3) 
        + right('000' + parsename(end_ip,1),3)
  • 相关阅读:
    VMware虚拟机中常见的问题汇总
    Windows10下安装VMware虚拟机并搭建CentOS系统环境
    myeclipse2017使用总结
    mybatis如何通过接口查找对应的mapper.xml及方法执行详解
    (转)将SVN从一台服务器迁移到另一台服务器(Windows Server VisualSVN Server)
    (转)Maven中的库(repository)详解 ---repository配置查找构件(如.jar)的远程库
    Git知识讲解
    (转)MyEclipse中使用git
    在SpringBoot中添加Logback日志处理
    (转)Spring Boot干货系列:(七)默认日志logback配置解析
  • 原文地址:https://www.cnblogs.com/mcmurphy/p/2748276.html
Copyright © 2011-2022 走看看