灯火互联
管理员
管理员
  • 注册日期2011-07-27
  • 发帖数41778
  • QQ
  • 火币41290枚
  • 粉丝1086
  • 关注100
  • 终身成就奖
  • 最爱沙发
  • 忠实会员
  • 灌水天才奖
  • 贴图大师奖
  • 原创先锋奖
  • 特殊贡献奖
  • 宣传大使奖
  • 优秀斑竹奖
  • 社区明星
阅读:3901回复:0

ASP优化篇——快速判断IP是电信OR网通

楼主#
更多 发布于:2012-09-10 21:39


判断IP的来源是电信还是网通,常用于站点的后台程序.典型的做法当然是查询数据库,数据库里保存着网通(或电信)的IP范围,然后通过范围搜索来决定该IP是否在网通(或电信)的区间内.不过每次都得查询数据库,效率显然很低.
      不多废话,直接开始说一种超快的算法吧.既然要极快,莫过于o(1)的复杂度,所以开辟一巨大的缓冲区,用经典的空间换时间,通过查表一步即可判定.那如何定义表的大小,还有key?先从CNC.txt这个文件看起.
(http://www.etherdream.com/FunnyScript/IPRange/CNC.txt)
      这个文件是中国网通的路由表.仔细观察下,不难发现掩码位数最高不超过24(即255.255.255.0).事实上24位的掩码是非常至少,毕竟一个网通的网段里只划分了256个ip,已是相当的少了.既然掩码位数最多只有24,所以ip的最后一位可以忽略不计,而ip的前3位共有256^3(=16M)的组合.所以将ip的前3位作为key,16M的表长度,正好定义出ip对应网段的表.可以形象如下表示:
    123.0.0.0/24 => Table[123.0.0] = TRUE
    202.0.0.0/16 => Table[202.0.0] = TRUE
                               Table[202.0.1] = TRUE
                               ...
                               Table[202.0.255] = TRUE
      检测的时候取IP前3位,检测表中对应是否为TRUE即可判断出此ip的类型.事实上,本例中ip只有电信和网通两种状态(非网通网段都当作电信),因此只需1bit即可保存每个记录.这时表占用的内存只需16M/8=2M.下面就用ASP来实现这功能.
      首先将路由表转化成一个2M大小的缓存表.考虑到ASP的运行速度,这里事先用c程序直接处理,然后保存为一个2M的二进制文件.ASP通过ADBDO.Stream读取数据流,并缓存在Appliction集合中.所谓的数据流其实也就是个Byte()变量,可以通过MidB,AscB这类二进制函数来处理.
      初始化函数:
Sub Init()  
    If LenB(Application("cnc")) Then  
        Exit Sub  
    End If  

    With Server.CreateObject("ADODB.Stream")  
        .Type = 1  
        .Open  
        .LoadFromFile Server.MapPath("CNC.dat")  
        Application("cnc") = .Read  
        .Close  
    End With  
End Sub  
      通过Appliction集合的缓存,就不必每次都读取文件.2M大小的内存也是可以接受的.接着就是分析IP地址,将其前3位转换成一个数字,因为这里每个记录是按位(bit)保存的,所以还要整除8来对应到Byte()的位置.最后通过Mod运算对应到具体字节的具体bit上.听着有些复杂,不过实现起来确是相当的简单:
Function IPIsCNC(IP)  
    Dim arr, val  
    Dim c  

    arr = Split(IP, ".")  
    val = CLng(arr(0)) * 65536 + CLng(arr(1)) * 256 + CLng(arr(2))  
    c = AscB(MidB(Application("cnc"), val \ 8 + 1, 1))  

    IPIsCNC = _  
        (c And 2^(val Mod 8)) <> 0  
End Function  
    IPIsCNC(IP),返回IP地址是否为网通.

    到此关键的两个函数就大功告成了,接着测试:
Sub Main()  
    On Error Resume Next  

    Init()  

    If Err Then  
        Response.Write "系统错误: " ; Err.Description  
        Exit Sub  
    End If  

    Dim IP  
    IP = Request.ServerVariables("REMOTE_ADDR")  

    If IPIsCNC(IP) Then  
        Response.Write IP ; "属于网通IP"  
    Else  
        Response.Write IP ; "属于电信IP"  
    End If  
End Sub  

Main()  
      考虑到Init函数需要文件的读取,所以添加了错误捕捉.不过通常情况下IPIsCNC是不会错误的,因为REMOTE_ADDR返回的必然是个正确格式的IP.
      每当访问ASP时,除了第一次需加载文件外,其余时候只需三四行代码既可以判定,真正实现了空间换时间.
      测试地址: http://www.etherdream.com/FunnyScript/IPRange/IP.ASP
      (另附上将路由表转换成缓存文件的c代


喜欢0 评分0
游客

返回顶部