Und wieder ein ganz kleines Code-Update, da die
IP-Adressen-Ermittlung nur funktionierte, wenn man eine
Indy-Net-Komponente schon irgendwo im Projekt eingebunden hatte:
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, IdSSLOpenSSL, 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
if (GStack=nil)
then
begin
var IdSSLIOHandlerSocketOpenSSL:TIdSSLIOHandlerSocketOpenSSL;
IdSSLIOHandlerSocketOpenSSL:=TIdSSLIOHandlerSocketOpenSSL.create();
end;
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;