'參考文檔:http://lumaqq.linuxsir.org/article/qqwry_format_detail.html
'作者: gounliey
'Email:4473117@qq.com
'第一次寫東西出來大家分享,還有很多不足之處,請大家指點。
'輔助類,用于保存IP索引信息
Public Class CZ_INDEX_INFO
Public IpSet As UInt32
Public IpEnd As UInt32
Public Offset As UInt32
Sub New()
IpSet = 0
IpEnd = 0
Offset = 0
End Sub
End Class
'讀取純真IP數據庫類
Public Class PHCZIP
Protected bFilePathInitialized As Boolean
Protected FilePath As String
Protected FileStrm As FileStream
Protected Index_Set As UInt32
Protected Index_End As UInt32
Protected Index_Count As UInt32
Protected Search_Index_Set As UInt32
Protected Search_Index_End As UInt32
Protected Search_Set As CZ_INDEX_INFO
Protected Search_Mid As CZ_INDEX_INFO
Protected Search_End As CZ_INDEX_INFO
Public Sub New()
bFilePathInitialized = False
End Sub
Public Sub New(ByVal dbFilePath As String)
bFilePathInitialized = False
SetDbFilePath(dbFilePath)
End Sub
'使用二分法查找索引區,初始化查找區間
Protected Sub Initialize()
Search_Index_Set = 0
Search_Index_End = Index_Count - 1
End Sub
'關閉文件
Public Sub Dispose()
If (bFilePathInitialized) Then
bFilePathInitialized = False
FileStrm.Close()
FileStrm.Dispose()
End If
End Sub
'設置純真IP數據庫的文件路徑
Public Function SetDbFilePath(ByVal dbFilePath As String) As Boolean
If (dbFilePath = "") Then
Return False
End If
'Try
'打開數據文件
FileStrm = New FileStream(dbFilePath, FileMode.Open, FileAccess.Read)
'Catch
'Return False
'End Try
'檢查文件長度
If (FileStrm.Length < 8) Then
FileStrm.Close()
FileStrm.Dispose()
Return False
End If
'得到第一條索引的絕對偏移和最后一條索引的絕對偏移
FileStrm.Seek(0, SeekOrigin.Begin)
Index_Set = GetUInt32()
Index_End = GetUInt32()
'得到總索引條數
Index_Count = (Index_End - Index_Set) / 7 + 1
bFilePathInitialized = True
Return True
End Function
'主接口函數,根據傳入的IP返回該IP的地址信息
Public Function GetAddressWithIP(ByVal IPvalue As String) As String
If Not bFilePathInitialized Then
Return ""
End If
Initialize()
'將IP轉化為數字
Dim ip As UInt32 = IPToUInt32(IPvalue)
While (True)
'首先初始化本輪查找的區間
'區間頭
Search_Set = IndexInfoAtPos(Search_Index_Set)
'區間尾
Search_End = IndexInfoAtPos(Search_Index_End)
'判斷IP是否在區間頭內
If (ip >= Search_Set.IpSet And ip <= Search_Set.IpEnd) Then
Return ReadAddressInfoAtOffset(Search_Set.Offset)
End If
'判斷IP是否在區間尾內
If (ip >= Search_End.IpSet And ip <= Search_End.IpEnd) Then
Return ReadAddressInfoAtOffset(Search_End.Offset)
End If
'計算出區間中點
Search_Mid = IndexInfoAtPos((Search_Index_End + Search_Index_Set) / 2)
'判斷IP是否在中點
If (ip >= Search_Mid.IpSet And ip <= Search_Mid.IpEnd) Then
Return ReadAddressInfoAtOffset(Search_Mid.Offset)
End If
'本輪沒有找到,準備下一輪
If (ip < Search_Mid.IpSet) Then
'IP比區間中點要小,將區間尾設為現在的中點,將區間縮小1倍。
Search_Index_End = (Search_Index_End + Search_Index_Set) / 2
Else
'IP比區間中點要大,將區間頭設為現在的中點,將區間縮小1倍。
Search_Index_Set = (Search_Index_End + Search_Index_Set) / 2
End If
End While
Return ""
End Function
'讀取指定文件偏移位置的國家和地區信息
Protected Function ReadAddressInfoAtOffset(ByVal Offset As UInt32) As String
Dim country As String = ""
Dim area As String = ""
Dim country_Offset As UInt32 = 0
Dim Tag As Byte = 0
'跳過4字節,因這4個字節是該索引的IP區間上限。
FileStrm.Seek(Offset + 4, SeekOrigin.Begin)
'讀取一個字節,得到描述國家信息的“尋址方式”
Tag = GetTag()
If (Tag = &H1) Then
'模式0x01,表示接下來的3個字節是表示偏移位置
FileStrm.Seek(GetOffset(), SeekOrigin.Begin)
'繼續檢查“尋址方式”
Tag = GetTag()
If (Tag = &H2) Then
'模式0x02,表示接下來的3個字節代表國家信息的偏移位置
'先將這個偏移位置保存起來,因為我們要讀取它后面的地區信息。
country_Offset = GetOffset()
'讀取地區信息(注:按照Luma的說明,好像沒有這么多種可能性,但在測試過程中好像有些情況沒有考慮到,
'所以寫了個ReadArea()來讀取。
area = ReadArea()
'讀取國家信息
FileStrm.Seek(country_Offset, SeekOrigin.Begin)
country = ReadString()
Else
'這種模式說明接下來就是保存的國家和地區信息了,以'\0'代表結束。
FileStrm.Seek(-1, SeekOrigin.Current)
country = ReadString()
area = ReadArea()
End If
ElseIf (Tag = &H2) Then
'模式0x02,說明國家信息是一個偏移位置
country_Offset = GetOffset()
'先讀取地區信息
area = ReadArea()
'讀取國家信息
FileStrm.Seek(country_Offset, SeekOrigin.Begin)
country = ReadString()
Else
'這種模式最簡單了,直接讀取國家和地區就OK了
FileStrm.Seek(-1, SeekOrigin.Current)
country = ReadString()
area = ReadArea()
End If
Return country + " " + area
End Function
'從當前位置讀取一個字符串(國家或者地區信息)
Protected Function ReadString() As String
Dim Offset As UInt32 = 0
Dim TempByteArray(256) As Byte
TempByteArray(Offset) = FileStrm.ReadByte()
While (TempByteArray(Offset) <> &H0)
Offset += 1
TempByteArray(Offset) = FileStrm.ReadByte()
End While
Return System.Text.Encoding.Default.GetString(TempByteArray)
End Function
'從當前文件位置讀取地區信息
Protected Function ReadArea() As String
Dim Tag As Byte = GetTag()
If (Tag = &H1 Or Tag = &H2) Then
FileStrm.Seek(GetOffset(), SeekOrigin.Begin)
Return ReadString()
Else
FileStrm.Seek(-1, SeekOrigin.Current)
Return ReadString()
End If
End Function
'從當前文件位置讀取一個字節的標識
Protected Function GetTag() As Byte
Return FileStrm.ReadByte()
End Function
'得到指定索引位置的信息(IP起始范圍)
Protected Function IndexInfoAtPos(ByVal Index_Pos As Int32) As CZ_INDEX_INFO
Dim Index_Info As New CZ_INDEX_INFO
'根據索引編號計算出在文件中在偏移位置
FileStrm.Seek(Index_Set + 7 * Index_Pos, SeekOrigin.Begin)
Index_Info.IpSet = GetUInt32()
Index_Info.Offset = GetOffset()
FileStrm.Seek(Index_Info.Offset, SeekOrigin.Begin)
Index_Info.IpEnd = GetUInt32()
Return Index_Info
End Function
'讀取3個字節,得到偏移位置
Protected Function GetOffset() As UInt32
Dim TempByte4(4) As Byte
TempByte4(0) = FileStrm.ReadByte()
TempByte4(1) = FileStrm.ReadByte()
TempByte4(2) = FileStrm.ReadByte()
TempByte4(3) = 0
Return BitConverter.ToUInt32(TempByte4, 0)
End Function
Protected Function GetUInt32() As UInt32
Dim TempByte4(4) As Byte
FileStrm.Read(TempByte4, 0, 4)
Return BitConverter.ToUInt32(TempByte4, 0)
End Function
'將字符串的IP轉換成Int32
Public Shared Function IPToUInt32(ByVal Ipvalue As String) As UInt32
Dim IpByte() As String = Ipvalue.Split(".")
Dim nUpperBound As Int32 = IpByte.GetUpperBound(0)
If nUpperBound <> 3 Then
ReDim Preserve IpByte(4)
For i As Int32 = 1 To 3 - nUpperBound
IpByte(nUpperBound + i) = "0"
Next
End If
Dim TempByte4(4) As Byte
For i As Int32 = 0 To 3
'如果是.Net 2.0可以支持TryParse。
'If Not (Byte.TryParse(IpByte(i), TempByte4(3 - i))) Then
'TempByte4(3 - i) = &H0
'End If
If (IsNumeric(IpByte(i))) Then
TempByte4(3 - i) = CInt(IpByte(i)) And &HFF
End If
Next
Return BitConverter.ToUInt32(TempByte4, 0)
'System.Net.IPAddress.NetworkToHostOrder
End Function
'將Int32型的IP轉換成字符串
Public Shared Function UInt32ToIP(ByVal UInt32value As UInt32) As String
Return New System.Net.IPAddress(System.Net.IPAddress.HostToNetworkOrder(UInt32value)).ToString()
End Function
End Class