纯真IP数据库解析Delphi D10.1下正常使用

 

直接一个单元,代码分享出来。

unit   Net.IPLocation;

interface

uses System.Classes, System.SysUtils, Winapi.WinSock, Vcl.Forms,
  System.Math, System.SyncObjs;

type
  TIPLocation = class(TObject)
  private
    QQWryFileName: string;
    QQWryFileStream: TBufferedFileStream;
    QQWryFileSize: Cardinal;
    IPRecordNum: Cardinal;
    FirstIPIndexOffset, LastIPIndexOffset: Cardinal;
    FLock: TCriticalSection;

    function GetQQWryFileName: string;
    function GetQQWryFileSize: Cardinal;
    function GetIPRecordNum: Cardinal;
    function GetQQWryDate: TDate;
    function GetQQWryDataFrom: string;
    function GetIPLocation(IPLocationOffset: Cardinal): TStringlist;
    function GetIPMsg(IPRecordID: Cardinal): TStringlist;
    function GetIPRecordID(IP: string): Cardinal;
    function GetIPValue(IP: string): Cardinal;
  public
    constructor Create(cQQWryFileName: string);
    destructor Destroy; override;
    function GetLocation(IP: string): String;
  end;

function IPLocation: TIPLocation;

implementation

var
  __IPLocation: TIPLocation;

function IPLocation: TIPLocation;
begin
  if __IPLocation = nil then
    __IPLocation := TIPLocation.Create(ExtractFilePath(ParamStr(0)) +
      'qqwry.dat');

  Result := __IPLocation;
end;

{ TIPLocation }

constructor TIPLocation.Create(cQQWryFileName: string);
begin
  inherited Create;
  FLock := TCriticalSection.Create;
  QQWryFileName := cQQWryFileName;
  QQWryFileStream := TBufferedFileStream.Create(QQWryFileName,
    fmOpenRead or fmShareDenyWrite, 0);
  QQWryFileSize := QQWryFileStream.Size;
  QQWryFileStream.Read(FirstIPIndexOffset, 4);
  QQWryFileStream.Read(LastIPIndexOffset, 4);
  IPRecordNum := (LastIPIndexOffset - FirstIPIndexOffset) div 7 + 1;
end;

destructor TIPLocation.Destroy;
begin

  QQWryFileStream.Free;
  FLock.Free;
  inherited Destroy;
end;

function TIPLocation.GetIPLocation(IPLocationOffset: Cardinal): TStringlist;
const
  // 实际信息字串存放位置的重定向模式
  REDIRECT_MODE_1 = 1;
  REDIRECT_MODE_2 = 2;
var
  RedirectMode: byte;
  CountryFirstOffset, CountrySecondOffset: Cardinal;
  CountryMsg, AreaMsg: string;
  //
  function ReadString(StringOffset: Cardinal): ansistring;
  var
    ReadByte: ansichar;
  begin
    Result := '';
    QQWryFileStream.Seek(StringOffset, soFromBeginning);
    QQWryFileStream.Read(ReadByte, 1);
    while ord(ReadByte) <> 0 do
    begin
      Result := Result + ReadByte;
      QQWryFileStream.Read(ReadByte, 1);
    end;
  end;
//
  function ReadArea(AreaOffset: Cardinal): ansistring;
  var
    ModeByte: byte;
    ReadAreaOffset: Cardinal;
  begin
    ReadAreaOffset := 0;
    QQWryFileStream.Seek(AreaOffset, soFromBeginning);
    QQWryFileStream.Read(ModeByte, 1);
    if (ModeByte = REDIRECT_MODE_1) or (ModeByte = REDIRECT_MODE_2) then
    begin
      QQWryFileStream.Read(ReadAreaOffset, 3);
      if ReadAreaOffset = 0 then
        Result := '未知地区'
      else
        Result := ReadString(ReadAreaOffset);
    end
    else
    begin
      Result := ReadString(AreaOffset);
    end;
  end;

begin
  CountryFirstOffset := 0;
  CountrySecondOffset := 0;
  // 跳过4个字节,该4字节内容为该条IP信息里IP地址段中的终止IP值
  QQWryFileStream.Seek(IPLocationOffset + 4, soFromBeginning);
  // 读取国家信息的重定向模式值
  QQWryFileStream.Read(RedirectMode, 1);
  // 重定向模式1的处理
  if RedirectMode = REDIRECT_MODE_1 then
  begin
    // 模式值为1,则后3个字节的内容为国家信息的重定向偏移值
    QQWryFileStream.ReadData(CountryFirstOffset, 3);
    // 进行重定向
    QQWryFileStream.Seek(CountryFirstOffset, soFromBeginning);
    // 第二次读取国家信息的重定向模式
    QQWryFileStream.Read(RedirectMode, 1);
    // 第二次重定向模式为模式2的处理
    if RedirectMode = REDIRECT_MODE_2 then
    begin
      // 后3字节的内容即为第二次重定向偏移值
      QQWryFileStream.ReadData(CountrySecondOffset, 3);
      // 读取第二次重定向偏移值下的字符串值,即为国家信息
      CountryMsg := ReadString(CountrySecondOffset);
      // 若第一次重定向模式为1,进行重定向后读取的第二次重定向模式为2,
      // 则地区信息存放在第一次国家信息偏移值的后面
      QQWryFileStream.Seek(CountryFirstOffset + 4, soFromBeginning);
      // 第二次重定向模式不是模式2的处理
    end
    else
    begin
      CountryMsg := ReadString(CountryFirstOffset);
    end;
    // 在重定向模式1下读地区信息值
    AreaMsg := ReadArea(QQWryFileStream.Position);
    // 重定向模式2的处理
  end
  else if RedirectMode = REDIRECT_MODE_2 then
  begin
    QQWryFileStream.ReadData(CountrySecondOffset, 3);
    CountryMsg := ReadString(CountrySecondOffset);
    AreaMsg := ReadArea(IPLocationOffset + 8);
    // 不是重定向模式的处理,存放的即是IP地址信息
  end
  else
  begin
    CountryMsg := ReadString(QQWryFileStream.Position - 1);
    AreaMsg := ReadArea(QQWryFileStream.Position);
  end;
  Result := TStringlist.Create;
  Result.Add(CountryMsg);
  Result.Add(AreaMsg);
end;

function TIPLocation.GetIPMsg(IPRecordID: Cardinal): TStringlist;
var
  aryStartIP: array [1 .. 4] of byte;
  strStartIP: string;
  EndIPOffset: Cardinal;
  aryEndIP: array [1 .. 4] of byte;
  strEndIP: string;
  i: integer;
begin
  EndIPOffset := 0;

  // 根据记录ID号移到该记录号的索引处
  QQWryFileStream.Seek(FirstIPIndexOffset + (IPRecordID - 1) * 7,
    soFromBeginning);
  // 索引的前4个字节为起始IP地址
  QQWryFileStream.Read(aryStartIP, 4);
  // 后3个字节是内容区域的偏移值
  // QQWryFileStream.Read(EndIPOffset, 3);
  QQWryFileStream.ReadData(EndIPOffset, 3);
  // 移至内容区域
  QQWryFileStream.Seek(EndIPOffset, soFromBeginning);
  // 内容区域的前4个字节为终止IP地址
  QQWryFileStream.Read(aryEndIP, 4);

  // 将起止IP地址转换为点分的形式
  strStartIP := '';
  for i := 4 downto 1 do
  begin
    if i <> 1 then
      strStartIP := strStartIP + IntToStr(aryStartIP[i]) + '.'
    else
      strStartIP := strStartIP + IntToStr(aryStartIP[i]);
  end;
  strEndIP := '';
  for i := 4 downto 1 do
  begin
    if i <> 1 then
      strEndIP := strEndIP + IntToStr(aryEndIP[i]) + '.'
    else
      strEndIP := strEndIP + IntToStr(aryEndIP[i]);
  end;
  Result := TStringlist.Create;
  Result.Add(strStartIP);
  Result.Add(strEndIP);
  // 获取该条记录下的IP地址信息
  // 以下三者是统一的:①内容区域的偏移值 ②终止IP地址的存放位置 ③国家信息紧接在终止IP地址存放位置后
  Result.AddStrings(GetIPLocation(EndIPOffset));
end;

function TIPLocation.GetIPRecordID(IP: string): Cardinal;
  function SearchIPRecordID(IPRecordFrom, IPRecordTo, IPValue: Cardinal)
    : Cardinal;
  var
    CompareIPValue1, CompareIPValue2: Cardinal;
  begin
    Result := 0;
    CompareIPValue1 := 0;
    CompareIPValue2 := 0;
    QQWryFileStream.Seek(FirstIPIndexOffset + ((IPRecordTo - IPRecordFrom) div 2
      + IPRecordFrom - 1) * 7, soFromBeginning);
    QQWryFileStream.Read(CompareIPValue1, 4);
    QQWryFileStream.Seek(FirstIPIndexOffset + ((IPRecordTo - IPRecordFrom) div 2
      + IPRecordFrom) * 7, soFromBeginning);
    QQWryFileStream.Read(CompareIPValue2, 4);
    // 找到了
    if (IPValue >= CompareIPValue1) and (IPValue < CompareIPValue2) then
    begin
      Result := (IPRecordTo - IPRecordFrom) div 2 + IPRecordFrom;
    end
    else
      // 后半段找
      if IPValue > CompareIPValue1 then
      begin
        Result := SearchIPRecordID((IPRecordTo - IPRecordFrom) div 2 +
          IPRecordFrom + 1, IPRecordTo, IPValue);
      end
      else
        // 前半段找
        if IPValue < CompareIPValue1 then
        begin
          Result := SearchIPRecordID(IPRecordFrom, (IPRecordTo - IPRecordFrom)
            div 2 + IPRecordFrom - 1, IPValue);
        end;
  end;

begin
  Result := SearchIPRecordID(1, GetIPRecordNum, GetIPValue(IP));
end;

function TIPLocation.GetIPRecordNum: Cardinal;
begin
  Result := IPRecordNum;
end;

function TIPLocation.GetIPValue(IP: string): Cardinal;
var
  tsIP: TStringlist;
  i: integer;
  function SplitStringToStringlist(aString: string; aSplitChar: string)
    : TStringlist;
  begin
    Result := TStringlist.Create;
    while pos(aSplitChar, aString) > 0 do
    begin
      Result.Add(copy(aString, 1, pos(aSplitChar, aString) - 1));
      aString := copy(aString, pos(aSplitChar, aString) + 1,
        length(aString) - pos(aSplitChar, aString));
    end;
    Result.Add(aString);
  end;

begin
  tsIP := SplitStringToStringlist(IP, '.');
  Result := 0;
  for i := 3 downto 0 do
  begin
    Result := Result + StrToInt(tsIP[i]) * trunc(power(256, 3 - i));
  end;
end;

function TIPLocation.GetLocation(IP: string): String;
begin
  FLock.Enter;
  try
    Result := GetIPMsg(GetIPRecordID(IP))[2];
  finally
    FLock.Leave;
  end;
end;

function TIPLocation.GetQQWryDataFrom: string;
begin
  Result := GetIPMsg(GetIPRecordNum)[2];
end;

function TIPLocation.GetQQWryDate: TDate;
var
  DateString: string;
begin
  DateString := GetIPMsg(GetIPRecordNum)[3];
  DateString := copy(DateString, 1, pos('IP数据', DateString) - 1);
  DateString := StringReplace(DateString, '年', '-',
    [rfReplaceAll, rfIgnoreCase]);
  DateString := StringReplace(DateString, '月', '-',
    [rfReplaceAll, rfIgnoreCase]);
  DateString := StringReplace(DateString, '日', '-',
    [rfReplaceAll, rfIgnoreCase]);
  Result := StrToDate(DateString);
end;

function TIPLocation.GetQQWryFileName: string;
begin
  Result := QQWryFileName;
end;

function TIPLocation.GetQQWryFileSize: Cardinal;
begin
  Result := QQWryFileSize;
end;

initialization

finalization

if __IPLocation <> nil then
  __IPLocation.Free;

end.

 

分享到: