David
发布于 2014-07-25 / 4 阅读 / 0 评论 / 0 点赞

VB.NET 讀取純真IP數據庫

博客剛弄好幾天,黑客比搜索引擎還快找上門來,這讓小白好大壓力。 難道WordPress的安全性已經低得不能再低了麼?真搞不懂他是怎麼找到我的。 從後台日誌裡看到關於這位仁兄的入侵過程,看上去有幾千條記錄,感覺IP地址來來去去也就那麼幾個,估計那位仁兄是拿我的博客練練手的。 各種URL注入就不多說了,不知道他還會不會來,都是中國人,何必為難自己人呢? 既然有IP地址,不如來查下這些IP地址都是哪裡的,看看那位仁兄用哪裡的電腦攻擊的。 經過強大的Excel過濾,幾千條記錄去除重複只剩下幾條記錄,還可以做下數據透視表,分析攻擊頻率和攻擊次數,現在才發現以前學的辦公軟件使用沒有白費,好東西啊。 篩選出來的結果就不貼了,免得被別有用心的人利用。 儘管真正的IP地址也就幾條,但我仍然不願意一條條去查歸屬地,試試搜下Excel能不能做自動查歸屬地的功能,後來發現自己SB了,這都能想到。 但是無獨有偶,被我遇到C#調用純真IP數據庫的示例。這個主意不錯,可以不用網頁,減少對網絡的依賴性。當年珊瑚QQ也是用這個數據庫做大,可謂是紅透半邊天啊,現在珊瑚QQ早已消失,可純真的還在更新,不得不佩服他們的毅力。贊一個。 根據那個示例是調用網上開源的DLL進行讀取數據庫的,使用起來超級簡單,只要設置數據庫文件路徑,然後就可以傳入IP地址進行查詢。 先附上示例代碼的下載鏈接(包含3個版本):地址 原示例代碼:
Stopwatch stopwatch = new Stopwatch();
  List<string> ips = new List<string> { "218.5.3.128", "120.67.217.7", "125.78.67.175", "220.250.64.23", "218.5.3.128", "120.67.217.7", "125.78.67.175", "220.250.64.23" };
  stopwatch.Start();
  for (int i = 0; i < 100; i++)
  {
     foreach (string item in ips)
     {
          ip = qqWry.Query(item);
        // Console.WriteLine("{0} {1} {2}", ip.IP, ip.Country, ip.Local);
    }
 }
  
 stopwatch.Stop();
 Console.WriteLine("QQWryLocator 花了{0} ms", stopwatch.ElapsedMilliseconds);
  
 stopwatch.Reset();
 stopwatch.Start();
 for (int i = 0; i < 100; i++)
 {
    foreach (string item in ips)
    {
        string s = IPLocation.IPLocation.IPLocate("qqwry.dat", item);
       // Console.WriteLine(s);
    }
 }
 stopwatch.Stop();
 Console.WriteLine("IPLocation 花了{0} ms", stopwatch.ElapsedMilliseconds);
原示例截圖: image_thumb_2
  原示例已經在IPLocation基礎上進行優化得到了更好的性能,這裡用到的算法非常簡單(第二次面試的時也有過這樣的試題,但想不通怎麼更好地解決單數的問題,人外有人,總有人會想到的) 算法這東西說理論很難說明,我還是舉個例子說吧,就用我面試的那個試題吧。 你有100個蘋果,一個天枰,其中一個蘋果比較輕,要求用最少比較次數找到這個蘋果。 按照一般情況下,肯定是一個個蘋果比較,要經過99次才能找到,明顯不符和要求,而且很慢。 那麼我們可以把這100個蘋果分成兩組,每組50個(當時我想那天枰得有多大)進行比較。(50:50) - 1 找到比較輕的那組,然後將那組再分成兩組25再比較。(25:25) - 2 繼續分的話會遇到單數問題,直接把多出來的拿兩個出來單獨比較找到較輕的那個,放一邊。(1:1) - 3 如此類推 24:24  -  4 12:12  -  5 6:6   -  6 3:3   -   7 1:1   -  8...............這裡比較出來的就要與前邊第三次出來的蘋果進行比較 1:1   -  9 2:2  -  10 1:1   -  11 這樣較輕的蘋果就出來了,然後你就可以去找老闆換一個大的。 原本要比較99次,現在只需要11次就可以找到了,這算是初步感受算法的魅力,的確很強大。 算法講完了,回到程序來。 原示例已經將耗時縮減80%,但有些網友還不滿足,感覺仍然有點慢,繼續優化下。 這裡就出現了新的版本,算法方面沒有改變,改的是算法所用到的一些方法。 比較蘋果還是用原來的方式,但我加快手腳搬動那些蘋果,從而節省大量時間。 原示例代碼即將優化的代碼:
 private long GetStartIp(long left, out long endIpOff)
        {
            long leftOffset = firstStartIpOffset + (left * 7L);
            byte[] buffer = new byte[7];
            Array.Copy(data, leftOffset, buffer, 0, 7);
            endIpOff = (Convert.ToInt64(buffer[4].ToString()) + (Convert.ToInt64(buffer[5].ToString()) * 0x100L)) + ((Convert.ToInt64(buffer[6].ToString()) * 0x100L) * 0x100L);
            return ((Convert.ToInt64(buffer[0].ToString()) + (Convert.ToInt64(buffer[1].ToString()) * 0x100L)) + ((Convert.ToInt64(buffer[2].ToString()) * 0x100L) * 0x100L)) + (((Convert.ToInt64(buffer[3].ToString()) * 0x100L) * 0x100L) * 0x100L);
        }
        private long GetEndIp(long endIpOff, out int countryFlag)
        {
            byte[] buffer = new byte[5];
            Array.Copy(data, endIpOff, buffer, 0, 5);
            countryFlag = buffer[4];
            return ((Convert.ToInt64(buffer[0].ToString()) + (Convert.ToInt64(buffer[1].ToString()) * 0x100L)) + ((Convert.ToInt64(buffer[2].ToString()) * 0x100L) * 0x100L)) + (((Convert.ToInt64(buffer[3].ToString()) * 0x100L) * 0x100L) * 0x100L);
        }
代碼裡有很多這種“0x100L”的地方,而這兩個方法又是100%用在算法上。這裡的相乘屬於操縱數據起始位置和結束位置,具體要到什麼位置就要看數據是什麼的格式了。 使用Convert.ToInt64把性能拖了一大截,還加上了乘法,效率再次下降,如果將Convert.ToInt64改為強制轉換,乘法改為移位,那麼性能將會提升到新的台階。 優化後代碼
 private uint GetStartIp(uint left, out uint endIpOff)
        {
            int leftOffset = (int)(firstStartIpOffset + (left * 7));
            endIpOff = (uint)data[4 + leftOffset] + (((uint)data[5 + leftOffset]) << 8) + (((uint)data[6 + leftOffset]) << 16);
            return (uint)data[leftOffset] + (((uint)data[1 + leftOffset]) << 8) + (((uint)data[2 + leftOffset]) << 16) + (((uint)data[3 + leftOffset]) << 24);
        }

        private uint GetEndIp(uint endIpOff, out int countryFlag)
        {
            countryFlag = data[4 + endIpOff];
            return (uint)data[endIpOff] + (((uint)data[1 + endIpOff]) << 8) + (((uint)data[2 + endIpOff]) << 16) + (((uint)data[3 + endIpOff]) << 24);
        }
原示例代碼中還有很多地方用乘法的地方,優化都是改成上面的方法,就不多說了。 3個版本執行速度比較,2.0版完爆之前的兩個版本。 图像 001 OK,理解地差不多了,就要動手我目的,批量查詢IP地址歸屬地。很久沒用VB.NET,就把C#轉換成VB.NET吧,以免忘記不會寫VB了。 轉過來後,會出現C#的強制轉換Long轉Integer到VB.NET下不能正常運行,關於這個明天再說,要睡了。 附上轉換好了的VB.NET代碼:純真數據庫應用 編寫環境VS2012     .NET4