Einzelnen Beitrag anzeigen

philipp.hofmann

Registriert seit: 21. Mär 2012
Ort: Hannover
859 Beiträge
 
Delphi 10.4 Sydney
 
#7

AW: Status: Tethering - noch Probleme mit MacOS und iOS

  Alt 9. Jun 2021, 16:12
Status: Tethering

- die Windows-App kann sowohl andere Manager und Profiles im Netzwerk suchen und finden (discover) als auch gefunden werden und dann zwischen beiden Apps Daten austauschen

- die Android-App kann sowohl andere Manager und Profiles im Netzwerk suchen und finden (discover) als auch gefunden werden und dann zwischen beiden Apps Daten austauschen
- Permission Internet muss auf "true" gesetzt sein

- die iOS-App kann andere Manager und Profiles im Netzwerk suchen und finden (discover), diesem auch Daten schicken und Antworten empfangen, kann aber unter iOS 14.x nicht gefunden werden (<iOS 14 funktioniert).
Ursache: DoOnReceiveData wird unter iOS 14.x nie errreicht (gleiches gilt für MacOS 11.x)
- Es muss via https://developer.apple.com/contact/...king-multicast die Berechtigung erfragt werden, das Entitlement Multicast zu setzen,
damit iOS 14.x funktioniert; im Projekt ist das Entitlement aber nicht zu setzen, dies passiert über das Provision Profile automatisch (außer man erzeugt
die Entitlement-Datei manuell);

Delphi-Quellcode:
  <key>com.apple.developer.networking.multicast</key>
  <true/>
- die MacOS-App kann andere Manager und Profiles im Netzwerk suchen und finden (discover), diesem auch Daten schicken und Antworten empfangen, kann aber unter MacOS 11.x nicht gefunden werden (<MacOS 11.x funktioniert).
Ursache: DoOnReceiveData wird unter MacOS 11.x nie errreicht (gleiches gilt für iOS 14.x)

Folgende Entitlements wurden gesetzt (Entitlement.TemplateOSX.xml oder via Projekt-Optionen):

Delphi-Quellcode:
  <key>com.apple.security.network.client</key>
  <true/>
  <key>com.apple.security.network.server</key>
  <true/>
Das Ticket für das iOS 14.x und MacOS 11.x-Problem ist unter https://quality.embarcadero.com/browse/RSP-34147 eingestellt.

Tipp 1: Formulardesigner: das Profile auf Enabled setzen und den Manager aus Disabled und dem Profile keinen Manager zuweisen.
Im Programm-Code den Manager auf Enabled setzen und direkt davor dem Profile den Manager zuweisen.
Sonst wird schon sehr viel initialisert (z.B. der Netzwerk-Adapter) bevor man erstmalig Zugriff im Code auf die Komponenten hat.

Delphi-Quellcode:
  CommandApp.Manager := CommandManager;
  CommandManager.Enabled := True;
Tipp 2: für iOS müssen eventuell alle Exception gefangen werden, weil ansonsten das nicht erfolgreiche binden eines Sockets zum Abbruch führen kann
Socket-Fehler # 48Adresse wird bereits verwendet:
$0000000100BCFDD0 _ZN7Idstack8TIdStack16RaiseSocketErrorEi + 316
$0000000100BD0B74 _ZN7Idstack8TIdStack20RaiseLastSocketErrorEv + 72
$0000000100BD0A18 _ZN7Idstack8TIdStack19CheckForSocketErrorEi + 44
$0000000100BCBD00 _ZN15Idstackvclposix16TIdStackVCLPosix4BindEiN6Sys tem13UnicodeStringEtN8Idglobal12TIdIPVersionE + 344
$0000000100BD99E8 _ZN14Idsockethandle15TIdSocketHandle4BindEv + 316
$0000000100BF84F8 _ZN17Idcustomtcpserver18TIdCustomTCPServer14StartL isteningEv + 188

Delphi-Quellcode:
  Application.OnException:=TgoExceptionReporter.ExceptionHandler;
  TMessageManager.DefaultManager.SubscribeToMessage(TgoExceptionReportMessage, HandleExceptionReport);
Tipp 3: IP-Adresse des eigenen Adapters nicht automatisch setzen lassen, sondern selbst per Indy auswählen:

Delphi-Quellcode:
uses IdStack, IdGlobal, System.Classes;
class var prefNetworkList:TStringList;

class function getIPv4Address():String;

function checkForPrefNetwork(value:String):boolean;
var i:integer;
begin
  Result:=false;
  for i:=0 to prefNetworkList.Count-1 do
  begin
    if (pos(prefNetworkList[i],value)=1) then
    begin
      Result:=true;
      exit;
    end;
  end;
end;

var
  AAddresses: TIdStackLocalAddressList;
  LAddr: TIdStackLocalAddress;
  I: Integer;
  IP: String;
begin
  AAddresses:=TIdStackLocalAddressList.Create();
  Result:='';
  if (prefNetworkList=nil) then
  begin
    prefNetworkList:=TStringList.Create();
    prefNetworkList.Add('192.168.');
  end;
  try
    GStack.GetLocalAddressList(AAddresses);
    for I := 0 to AAddresses.Count-1 do
    begin
      LAddr:=AAddresses[I];
      IP:=LAddr.IPAddress;
      if ((IP<>'127.0.0.1') and ((Result='') or ((prefNetworkList.Count>0) and (checkForPrefNetwork(IP)) and (not checkForPrefNetwork(result))))) then
      begin
        case LAddr.IPVersion of
          Id_IPv4:
          begin
            log.d('Found IP-address*: '+IP);
            Result:=IP;
          end;
          Id_IPv6:
          begin
            log.d('Found IP-address: '+IP);
          end;
        end;
      end else
        log.d('Found IP-address: '+IP);
    end;
  finally
    AAddresses.Free;
  end;
end;
Dies passiert an zwei Stellen:

System.Tether.Manager.pas
Delphi-Quellcode:
constructor TTetheringAdapter.Create;
var ip:String;
begin
  inherited;
  FRemoteManagers := TTetheringManagerInfoList.Create;
  ip:=getIPv4Address();
  if (ip>'') then
    FAdapterConnectionString := ip
  else
    FAdapterConnectionString := TTetheringManagerCommunicationThread.EMPTYTOKEN;
end;
System.Tether.NetworkAdapter.pas
Delphi-Quellcode:
procedure TTetheringNetworkManagerCommunicationThread.Execute;
...
        if not LListening then
          raise ETetheringException.Create(SManagerNetworkCreation);

        var ip:String;
        ip:=TStringUtils.getIPv4Address();
        if (ip>'') then
          LRemoteConnectionString := ip + TetheringConnectionSeparator + FTarget
        else
          LRemoteConnectionString := EMPTYTOKEN + TetheringConnectionSeparator + FTarget;
Zur Sicherheit habe ich es noch hier eingebaut, da wird es aber höchstwahrscheinlich nicht gebraucht:

System.Tether.NetworkAdapter.pas
Delphi-Quellcode:
procedure TTetheringNetworkAdapterCommon.DoDiscoverManagers(Timeout: Cardinal;
  const ATargetList: TTetheringTargetHosts; const AProfileGroups, AProfileTexts: TArray<string>);
...
    LCommand := TTetheringManagerCommand.Create(TTetheringNetworkManagerCommunicationThread.TetheringDiscoverManagers,
      FAdapterConnectionString, Manager.Version, [FCommunicationThread.FTarget, LGroups, LTexts]);
  end else begin
    if (pos(TTetheringNetworkManagerCommunicationThread.EMPTYTOKEN,FAdapterConnectionString)>0) then
    begin
      var ip:String;
      ip:=TStringUtils.getIPv4Address();
      if (ip>'') then
        FAdapterConnectionString:=StringReplace(FAdapterConnectionString,TTetheringNetworkManagerCommunicationThread.EMPTYTOKEN,ip,[]);
    end;
Tipp 4: Tethering mit iOS bei aktivierten Mobilen Daten:

Damit bei aktivierten Mobilen Daten unter iOS das Tethering funktioniert, muss die Funktion BroadcastData in System.Tether.Comm gefixt werden.
Dies ist auch als Issue bei EMBT eingestellt (https://quality.embarcadero.com/browse/RSP-31191).
Delphi-Quellcode:

procedure TTetheringNetworkServerCommUDP.BroadcastData(const AData: TBytes; const AHost: string;
  InitialPort, FinalPort: Integer);
var
  I, J: Integer;
  LHost: string;
  LData: TBytes;
  allWithException:boolean;
  toRaise:Exception;
begin
  if TTetheringAdapter.IsLoggingItem(TTetheringAdapter.TTetheringLogItem.Comm) then
      TLogAdapter.Log('** UDP ** BroadcastData to "' + AHost + '": "' + TEncoding.UTF8.GetString(AData) + '"');
    if AHost = 'then
    begin
      if FIPVersion = TCommIPVersion.IP_IPv4 then
        LHost := '255.255.255.255'
      else
        LHost := '0:0:0:0:0:0:255.255.255.255';
 
      allWithException:=true;
      toRaise:=nil;
      for J := 0 to FUDPServer.Bindings.Count - 1 do
      begin
        try
          LData := TEncoding.UTF8.GetBytes(StringReplace(
            TEncoding.UTF8.GetString(AData),
            '|' + TetheringMULTIHOMED + '$',
            '|' + FUDPServer.Bindings.Sockets[J].IP + '$',
            []));
          for I := InitialPort to FinalPort do
            FUDPServer.Bindings.Sockets[J].Broadcast(LData, I, LHost);
          allWithException:=false;
        except on E: Exception do
          begin
            if (toRaise=nil) then
              toRaise:=e;
          end;
        end;
      end;
      if (allWithException and (toRaise<>nil)) then
        raise(toRaise);
    end else
      for I := InitialPort to FinalPort do
        FUDPServer.SendBuffer(AHost, I, AData);
end;

Geändert von philipp.hofmann ( 9. Jun 2021 um 18:48 Uhr)
  Mit Zitat antworten Zitat