【问题标题】:How to make TIdStackLocalAddress work on Android?如何使 TIdStackLocalAddress 在 Android 上工作?
【发布时间】:2022-10-13 05:19:16
【问题描述】:

仿照@Remy Lebeauthis SO item 中非常有用的代码示例,我有以下函数,它在 Windows 10 中按预期工作返回;例如。:

  No of Addresses: 4
  IPv4 Addresses:
  IP Address #0: 192.168.56.1 - 255.255.255.0 - 11
  IP Address #1: 192.168.1.7 - 255.255.255.0 - 8
  IP: 192.168.56.1

但是,当我将平台更改为 Android 64 位并在我的三星 S21 上运行它,同时启用 WiFi 并连接到我的 LAN 时,它只返回本地环回 IP 而没有其他值;例如。:

No of Addresses: 1
IPv4 Addresses:
IP Address #0: 127.0.0.1 -  - 0
IP: 127.0.0.1

我曾希望这与缺乏一些许可有关,但正如 Remy 在评论中指出的那样,问题在于 Android 的 Indy10 方法已损坏,需要使用以下 Dave Nottage 的解决方法。 (如果您还想获得 Indy 方法应该返回的 NetMask,则需要进一步的工作。如果/当我把它弄出来时,我会在此处发布我的解决方案作为答案)

function getLocalIP: string;
begin
  Result := '';
  try
    var IPList := TIdStackLocalAddressList.Create;
    try
      TIdStack.IncUsage;
      try
        GStack.GetLocalAddressList(IPList);
      finally
        TIdStack.DecUsage;
      end;

      WriteLog('DEBUG', 'No of Addresses: ' + IntToStr(IPList.Count));
      WriteLog('DEBUG', 'IPv4 Addresses:');

      var IPStrings := TStringList.Create;
      try
        for var i in IPList do
        begin
          if TIdStackLocalAddressIPv4(i).IPVersion = Id_IPv4 then
          begin
            IPStrings.Add(TIdStackLocalAddressIPv4(i).IPAddress + ' - ' + TIdStackLocalAddressIPv4(i).SubNetMask
              + ' - ' + TIdStackLocalAddressIPv4(i).InterfaceIndex.ToString);
          end;
        end;

        // show IP Addresses in the log file
        for var i := 0 to IPStrings.Count-1 do
          WriteLog('DEBUG', 'IP Address #' + IntToStr(i) + ': ' + IPStrings[i]);
        Result := IPStrings[0].Split([' - '])[0];
        WriteLog('DEBUG', 'IP: ' + Result);
      finally
        IPStrings.Free;
      end;
    finally
      IPList.Free;
    end;
  except
    On E: Exception do
    begin
      Result := '';
      WriteLog('ERROR', 'IP Error: ' + E.message);
    end;
  end;
end;

【问题讨论】:

  • 我真的很感谢@DaveNottage 的快速回答,我希望他的代码能解决问题。但是,我注意到使用该代码将需要编译器指令来选择适合每个平台的源代码段(我最终也希望使用 iOS)。在戴夫的要点发布 5 年后,现在 TIdStack 是否仍然存在问题?
  • 感谢您确认它在 Android 中仍然存在问题。它现在适用于 iOS 吗?
  • AFAIK,是的。它在 iOS(以及 OSX、Linux 和 FreeBSD)上使用 getifaddrs()

标签: android delphi indy10


【解决方案1】:

根据this gist 但在这里重复:

interface

uses
  IdStack;

procedure GetLocalAddressList(const AAddresses: TIdStackLocalAddressList);

implementation

uses
  System.SysUtils,
  Androidapi.JNI.Java.Net, Androidapi.JNI.JavaTypes, Androidapi.Helpers,  Androidapi.JNIBridge,
  IdGlobal;

procedure GetLocalAddressList(const AAddresses: TIdStackLocalAddressList);
var
  LInterfaces, LAddresses: JEnumeration;
  LInterface: JNetworkInterface;
  LAddress: JInetAddress;
  LInet4Address: JInet4Address;
  LInet6Address: JInet6Address;
  LName, LHostAddress: string;
begin
  AAddresses.Clear;
  LInterfaces := TJNetworkInterface.JavaClass.getNetworkInterfaces;
  while LInterfaces.hasMoreElements do
  begin
    LInterface := TJNetworkInterface.Wrap(JObjectToID(LInterfaces.nextElement));
    LAddresses := LInterface.getInetAddresses;
    while LAddresses.hasMoreElements do
    begin
      LAddress := TJInetAddress.Wrap(JObjectToID(LAddresses.nextElement));
      if LAddress.isLoopbackAddress then
        Continue;
      // Hack until I can find out how to check properly
      LName := JStringToString(LAddress.getClass.getName);
      LHostAddress := JStringToString(LAddress.getHostAddress);
      // Trim excess stuff
      if LHostAddress.IndexOf('%') > -1 then
        LHostAddress := LHostAddress.Substring(0, LHostAddress.IndexOf('%'));
      if LName.Contains('Inet4Address') then
        TIdStackLocalAddressIPv4.Create(AAddresses, LHostAddress, '')
      else if LName.Contains('Inet6Address') then
        TIdStackLocalAddressIPv6.Create(AAddresses, LHostAddress);
    end;
  end;
end;

使用风险自负

【讨论】:

  • 感谢您指出我的要点。你有没有找到一种方法来检索地址的子网掩码?我也需要它,因为我的目标是构建广播地址。
  • 显然this 将是用于获取子网掩码的方法。
  • 谢谢你的指点,戴夫。现在这个 Android 新手只需要变得足够聪明,就能弄清楚如何将该方法添加到上述代码中。
【解决方案2】:

这是我根据@Dave Nottage 的要点提出的用于检索Android IPv4 地址和子网掩码的方法:

unit AndroidGetInetAddress;

interface

{$IFDEF Android}
uses
  System.SysUtils, {System.Types, System.UITypes, System.Classes, System.Variants,}
  IdStack{, IdBaseComponent, IdComponent, IdUDPBase, IdUDPClient, IdGlobal};

procedure GetLocalAddressList(const AAddresses: TIdStackLocalAddressList);
{$ENDIF}

implementation

{$IFDEF Android}

uses
  Androidapi.JNI.Java.Net, Androidapi.JNI.JavaTypes, Androidapi.Helpers,  Androidapi.JNIBridge;

{ Dumb conversion of number of bits to 4-byte SubnetMask string}
function SubnetMask(ACode: string): string;
begin
  var NCode := ACode.ToInteger;
  if NCode < 25 then
    Result := '255.255.255.' + (%11111111 shr NCode).ToString
  else if NCode < 33 then
    Result := '255.255.' + (%11111111 shr NCode-24).ToString + '.0'
  else if NCode < 41 then
    Result := '255.' + (%11111111 shr NCode-32).ToString + '.0.0'
  else if NCode < 49 then
    Result := (%11111111 shr NCode-40).ToString + '.0.0.0'
  else
    Result := '0.0.0.0.';
end;

procedure GetLocalAddressList(const AAddresses: TIdStackLocalAddressList);

begin
  AAddresses.Clear;
  var LInterfaces := TJNetworkInterface.JavaClass.getNetworkInterfaces;

  while LInterfaces.hasMoreElements do
  begin
    var LInterface := TJNetworkInterface.Wrap(TAndroidHelper.JObjectToID(LInterfaces.nextElement));

////// method getInetAddresses evidently does not return a subnet mask
//    var LAddresses := LInterface.getInetAddresses;
//    while LAddresses.hasMoreElements do
//    begin
//      var LAddress := TJInetAddress.Wrap(TAndroidHelper.JObjectToID(LAddresses.nextElement));
//      WriteLog('DEBUG', 'getAddress: ' + JStringToString(LAddress.ToString));
//      if LAddress.isLoopbackAddress then
//        Continue;
//      // Dave Nottage: Hack until I can find out how to check properly
//      WriteLog('DEBUG', 'LName: ' + JStringToString(LAddress.getClass.getName));
//      WriteLog('DEBUG', 'LHostAddress: ' + JStringToString(LAddress.getHostAddress));
//    end;

////// method getInterfaceAddresses does return IP address, mask, and '[null]' or broadcast IP
    var LInterfaceAddresses := LInterface.getInterfaceAddresses;
    for var i := 0 to LInterfaceAddresses.size-1 do
    begin
      var LHostAddress := JStringToString(LInterfaceAddresses.get(i).toString);
//      WriteLog('DEBUG', 'Address ' + i.ToString + ': ' + LHostAddress);
      if not LHostAddress.Contains('[null]') then
      // Have IPv4Inet address with subnet mask and broadcast address
      begin
        var Address := LHostAddress.Split(['/','[',']',' '], TStringSplitOptions.ExcludeEmpty);
        TIdStackLocalAddressIPv4.Create(AAddresses, Address[0], SubnetMask(Address[1]))
      end;
    end;
  end;
end;
{$ENDIF}

end.

“使用风险自负”:)

【讨论】:

    猜你喜欢
    • 2014-10-04
    • 2012-11-23
    • 2014-09-22
    • 1970-01-01
    • 2014-02-16
    • 2019-05-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多