网络 · 2023年11月23日

获取当前网络的公网IP(外网IP)

这个思路是需要连接某个服务器,由服务器返回当前连接的IP,从而获得当前网络的出口公网IP;

可提供查询的网址如下

  1. https://ipinfo.io/ip
  2. https://ifconfig.io/ip
  3. https://ifconfig.me/ip
  4. https://ipecho.net/plain
  5. http://icanhazip.com
  6. http://members.3322.org/dyndns/getip
  7. http://whatismyip.akamai.com

返回的数据主要有几类:

  1. 返回IP地址;这种直接就可以使用
  2. 返回JSON中带有IP地址; 需要进一步解析;
  3. 返回一个字符串; 需要进一步解析;

Delphi实现

unit uGetWLanIP;

interface

uses
  SysUtils, Classes, qjson, inifiles, uWinHttp, yxdworker;

 //同步调用获取公网IP(外网IP)
 function GetWLanIP:string;
 //异步调用获取公网IP(外网IP)
 procedure DoGetWLanIP(AJob:PJob);

var 
  GPublicIP : string;        //外网IP/公网IP XQ220812001

implementation

uses
  uLogUtils, PerlRegEx;

procedure DoGetWLanIP(AJob:PJob);
begin
  GPublicIP := GetWLanIP;
end;

function GetWLanIP:string;
var
  _file:Tinifile;
  _iCount, I:integer;
  _sValue, _sUrl, _sType, _sFieldName:string;
  _sIP:string;
  _lStrList:TstringList;
  function CheckIP(AIP:string):Boolean;
  var
    _rReg : TPerlRegEx;
  begin
    _rReg:= TPerlRegEx.Create; //创建实例
    try
      //设置源字符串
      _rReg.Subject:= AIP;
      //设置正规表达式格式
      _rReg.RegEx:= '^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$';
      //执行查找
      Result := _rReg.Match;
    finally
      //释放内存
      FreeAndNil(_rReg);
    end;
  end;
  function FromUrlGetIP(AUrl:string; AType:string; AJsonName:string=''):string;
  var
    _WinHttp: TWinHttp;
    _Qjson:TQjson;
    _sRes, _sStr:string;
  begin
    Result := '';
    if AUrl = '' then Exit;
    _WinHttp := TWinHttp.Create(nil);
    _Qjson := TQJson.Create;
    try
      try
        _WinHttp.ClearRequestHeaders;
        _WinHttp.Request.ContentType := 'application/json';
        _WinHttp.Request.Accept      := '*/*';
        _WinHttp.Request.CharSet     := 'utf-8';
        _WinHttp.Request.UserAgent   := 'Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)';
        _WinHttp.Request.AcceptLanguage := 'zh-cn';
        _WinHttp.RequestTime         := 3000;
        GSysLog.AddLog(Format('[获取公网IP] Request - %s', [AUrl]), ltError);
        _sRes := _WinHttp.Get(AUrl);
        if Length(_sRes) > 0 then
        begin
          GSysLog.AddLog(Format('[获取公网IP] Response - %s', [_sRes]), ltError);
          if AType = 'IP' then
            _sStr := Trim(_sRes)
          else if AType = 'JSON' then
          begin
            if not _Qjson.TryParse(_sRes) then Exit;
            if AJsonName = '' then Exit;
            _sStr := _Qjson.ValueByName(AJsonName,'');
          end
          else
            _sStr := '';
          //检查IP是否合法
          if CheckIP(_sStr) then
            Result := _sStr;
        end
        else
        begin
          GSysLog.AddLog(Format('[获取公网IP] TWinHttp Err - %s', [_WinHttp.LastErrMsg]), ltError);
        end;
      except
        on E:Exception do
        begin
          GSysLog.AddLog(Format('[获取公网IP] TWinHttp Err - %s', [E.Message]), ltError);
        end;
      end;
    finally
      FreeAndNil(_WinHttp);
      FreeAndNil(_Qjson);
    end;
  end;
begin
  Result := '';
  _sIP := '';
  //1. 读取配置文件 GetWLanIP.dat
  _file := Tinifile.Create(ExtractFilePath(paramstr(0))+'GetWLanIP.dat');
  _lStrList := TstringList.Create;
  //1.1 读取只返回IP的
  _iCount := _file.ReadInteger('URL', 'count', 0);
  for I := 1 to _iCount do
  begin
    _sValue := trim(_file.ReadString('URL', inttostr(I), ''));
    if _sValue = '' then Continue;
    //分割
    _lStrList.Delimiter := ',';
    _lStrList.DelimitedText  := _sValue;
    if _lStrList.Count <> 3 then Continue;
    _sUrl  := _lStrList.Strings[0];
    _sType := _lStrList.Strings[1];
    _sFieldName := _lStrList.Strings[2];
    //2. 调用WinHttp连接,获取数据
    _sIP := FromUrlGetIP(_sUrl, _sType, _sFieldName);
    if _sIP <> '' then Break;
  end;

  //未能正常获取到IP,则直接使用127.0.0.1填充 
  if _sIP = '' then
    Result := '127.0.0.1'
  else
    Result := _sIP;
end;




initialization
  GPublicIP  := '';

end.
Pascal