ertank 28 Posted January 13, 2020 (edited) Hello, I am using Delphi 10.3.3, Indy 10.6.2.5366 (stock Indy version) I am trying to listen broadcast UDP messages on Android (exact version I am testing is 4.4.2, I need to support latest versions, too) connected to a local WiFi. Broadcast messages will be sent in same network. I have found this page https://stackoverflow.com/questions/19040674/delphi-xe5-tidudpserver-does-not-receive-anything-on-android and tried to do it as to my understanding. Project has CHANGE_WIFI_MULTICAST_STATE permission set. UDP server parameters are as following at design time and everything else is left as default: TIdUDPServer.Active = False TIdUDPServer.BroadcastEnable = True TIdUDPServer.DefaultPort = 8080 I have following code where timer is activated as last line in OnFormCreate() event and has 0.5 seconds delay: procedure TForm3.tmrCheckPhoneServiceTimer(Sender: TObject); var WifiManagerObj: JObject; begin // Run just once tmrCheckPhoneService.Enabled := False; // Get necessary OS settings to receive broadcast messages Log('Getting multicast lock...'); WifiManagerObj := TAndroidHelper.Context.getSystemService(TJContext.JavaClass.WIFI_SERVICE); FWifiManager := TJWifiManager.Wrap((WifiManagerObj as ILocalObject).GetObjectID); FMulticastLock := FWifiManager.createMulticastLock(StringToJString('LightFactory Remote')); FMulticastLock.setReferenceCounted(True); FMulticastLock.acquire(); // Try to open a port on Android by sending a broadcast message Log('Sending a broadcast message...'); IdUDPServer1.Broadcast('test', IdUDPServer1.DefaultPort); // Now we can start to listen Log('Activating UDP listener...'); IdUDPServer1.Active := True; if FPhoneDialerService = nil then begin TDialogService.ShowMessage('PhoneDialer service not supported'); Exit(); end; PermissionsService.RequestPermissions([FCallPhonePermission], MakePhoneCallPermissionRequestResult, DisplayRationale); end; I have following code in my OnDestroy() event: procedure TForm3.FormDestroy(Sender: TObject); begin // Stop listenning. IdUDPServer1.Active := False; // Release multicast lock FMulticastLock.release(); end; My broadcast listening code is as following: procedure TForm3.IdUDPServer1UDPRead(AThread: TIdUDPListenerThread; const AData: TIdBytes; ABinding: TIdSocketHandle); var Data: string; begin Data := TEncoding.Default.GetString(AData); TThread.Queue(nil, procedure begin Log('Incoming broadcast message from: ' + ABinding.PeerIP) end); if SameText(Data, 'mytriggerphrase') then begin TThread.Queue(nil, procedure begin TestConnection(ABinding.PeerIP) end); end; end; After I run the app, I have following in my log: 2020-01-14 00:59:33.697 Getting multicast lock... 2020-01-14 00:59:33.736 Sending a broadcast message... 2020-01-14 00:59:33.740 Activating UDP listener... 2020-01-14 00:59:33.804 Incoming broadcast message from: 192.168.1.186 That IP number in above log belongs to Android device itself. I have following problems that I could not figure a solution: 1- I do not get any other broadcast message from my PC application which is sending one broadcast message each second. Is there anything I am doing wrong for that to happen? 2- I get segmentation fault (11) when closing my app. Detailed call stack and exact line is in attached picture. This happens each and every close. I did not understand why. Lastly, I am not sure if my code is doing it correct to keep multicast lock thru all run-time. I read a suggestion to release a multicast lock once finished with it in order to save battery life. So, I wonder if I can release multicast lock right after enabling my TIdUDPServer? Any help is appreciated. Thanks & regards, Ertan Edited January 14, 2020 by ertank Typing fix 1 Share this post Link to post
Dave Nottage 562 Posted January 14, 2020 As far as the MulticastLock goes, you should call acquire when your app needs to listen (i.e. when your TIdUPDServer is active), and release when it does not. That said, I've given up on Indy for multicast because there have been too many hoops to jump through, including for Android. Share this post Link to post
Remy Lebeau 1428 Posted January 14, 2020 (edited) I can't really comment on the multicast issue, especially on Android. But if you are really doing multicasting, why are you using TIdUDPServer? That is not a multicast-enabled component. Indy has separate TIdIPMCastClient and TIdIPMCastServer components specifically for multicast. For UDP, assuming a plain vanilla network broadcast is in fact, being used, are you setting up the TIdUDPServer.Bindings collection at all prior to activating the server? One problem I do see is your use of TThread.Queue() captures the ABinding object, which is no longer valid once the TIdUDPServer is freed. Delphi anonymous procedures capture variables, not values. You should use TThread.Queue() more like this instead: procedure TForm3.QueueLog(const AMsg: string); begin TThread.Queue(nil, procedure begin Log(AMsg); end ); end; procedure TForm3.QueueTestConnection(const APeerIP: string); begin TThread.Queue(nil, procedure begin TestConnection(APeerIP); end ); end; procedure TForm3.IdUDPServer1UDPRead(AThread: TIdUDPListenerThread; const AData: TIdBytes; ABinding: TIdSocketHandle); var Data: string; begin Data := TEncoding.Default.GetString(AData); QueueLog('Incoming broadcast message from: ' + ABinding.PeerIP); if SameText(Data, 'mytriggerphrase') then begin QueueTestConnection(ABinding.PeerIP); end; end; Edited January 14, 2020 by Remy Lebeau 2 Share this post Link to post
ertank 28 Posted January 15, 2020 23 hours ago, Remy Lebeau said: I can't really comment on the multicast issue, especially on Android. But if you are really doing multicasting, why are you using TIdUDPServer? That is not a multicast-enabled component. Indy has separate TIdIPMCastClient and TIdIPMCastServer components specifically for multicast. I did not know until I read your reply that there are dedicated components. I am going to try them. 23 hours ago, Remy Lebeau said: For UDP, assuming a plain vanilla network broadcast is in fact, being used, are you setting up the TIdUDPServer.Bindings collection at all prior to activating the server? Below is all I set on TIdUDPServer. Actually, I set these at design time and activate component at run-time as in my initial post. TIdUDPServer.Active := False; TIdUDPServer.BroadcastEnable := True; TIdUDPServer.DefaultPort := 8080; 23 hours ago, Remy Lebeau said: One problem I do see is your use of TThread.Queue() captures the ABinding object, which is no longer valid once the TIdUDPServer is freed. Delphi anonymous procedures capture variables, not values. You should use TThread.Queue() more like this instead: Using suggested code, I still get segmentation fault (11) when app exists. However, call stack changed a little this time as in attached screen shot. Share this post Link to post
Remy Lebeau 1428 Posted January 15, 2020 (edited) 1 hour ago, ertank said: Using suggested code, I still get segmentation fault (11) when app exists. However, call stack changed a little this time Do you have the same error if you manually deactivate the TIdUDPServer BEFORE exiting your app? That would allow Indy to clean up everything in a more deterministic manner. The TIdUDPServer destructor will normally deactivate the server and clean up, but there is no call to that destructor in your stack trace. The stack trace suggests the RTL is freeing a UDP listener thread that is still actively running, but crashes when the TIdThread destructor tries to decrement the global IdThread.GThreadCount counter, which has probably already been finalized and freed by the RTL before the listener thread is freed. So you should deactivate the server beforehand to ensure there are no threads running when the RTL does its own cleanup. Edited January 15, 2020 by Remy Lebeau Share this post Link to post
Dave Nottage 562 Posted January 16, 2020 On 1/14/2020 at 11:06 AM, Dave Nottage said: As far as the MulticastLock goes, you should call acquire when your app needs to listen (i.e. when your TIdUPDServer is active), and release when it does not. Incidentally, I suspect that leaving it "acquired" can consume a lot of battery (due to what happened to my device), so it might pay to check when the screen locks and do a release. Share this post Link to post
ertank 28 Posted January 18, 2020 On 1/16/2020 at 12:28 AM, Remy Lebeau said: Do you have the same error if you manually deactivate the TIdUDPServer BEFORE exiting your app? That would allow Indy to clean up everything in a more deterministic manner. The TIdUDPServer destructor will normally deactivate the server and clean up, but there is no call to that destructor in your stack trace. The stack trace suggests the RTL is freeing a UDP listener thread that is still actively running, but crashes when the TIdThread destructor tries to decrement the global IdThread.GThreadCount counter, which has probably already been finalized and freed by the RTL before the listener thread is freed. So you should deactivate the server beforehand to ensure there are no threads running when the RTL does its own cleanup. So far I have tried following on a fresh project using one TIdTCPServer and one TIdIPMCastServer: - Set Active property to False any Indy component on form manually way before closing app: Failed - Set Active property to False any Indy component on form manually. Free them way before closing app: Failed - Remove all auto-create components. Create them using code without owner. Set Active property of them to False way before closing app: Failed - Remove all auto-create components. Create them using code without owner. Set Active property of them to False. Free them at run-time way before closing app: Failed - Remove all auto-create components. Create them using code without owner. Set Active property of them to False. *Do not* Free them at run-time and close app: Failed (this one was just for my curiosity) I get very similar call stack for all tests above. Then I tried to do some tests using just a single component: - Using run-time created TIdTCPServer with no owner. Active property set to False at run-time. *Did not* free and closing app: No exception (just some memory leak obviously) - Using run-time created TIdTCPServer with no owner. Active property set to False at run-time. Freed before closing app: No exception - Using run-time created TIdTCPServer with no owner. Active property *not* set to False at run-time. Freed before closing app: No exception - Using design-time placed TIdTCPServer with no owner. Active property set to False at run-time way before closing app: Failed - Using run-time created TIdIPMCastServer with no owner. Active property set to True, used TIdIPMCastServer.Send() and property set to False. Freed at run-time way before closing app: No exception It is interesting that using either component alone created at run-time is not failing at all. Mixing them both seems to be having a problem. Using TIdUDPClient for sending broadcast messages with TIdTCPServer also failing. In anyway, it seems to be best creating components at run-time as putting them on form at design time is always failing. Share this post Link to post
Remy Lebeau 1428 Posted January 20, 2020 On 1/18/2020 at 2:56 PM, ertank said: So far I have tried following on a fresh project using one TIdTCPServer and one TIdIPMCastServer: - Set Active property to False any Indy component on form manually way before closing app: Failed - Set Active property to False any Indy component on form manually. Free them way before closing app: Failed - Remove all auto-create components. Create them using code without owner. Set Active property of them to False way before closing app: Failed - Remove all auto-create components. Create them using code without owner. Set Active property of them to False. Free them at run-time way before closing app: Failed - Remove all auto-create components. Create them using code without owner. Set Active property of them to False. *Do not* Free them at run-time and close app: Failed (this one was just for my curiosity) Failed HOW exactly? Please be more specific. On 1/18/2020 at 2:56 PM, ertank said: I get very similar call stack for all tests above. I would need to see the actual call stacks. TIdTCPServer is a very different component than TIdUDPServer, which is a very component from TIdIPMCastServer. On 1/18/2020 at 2:56 PM, ertank said: It is interesting that using either component alone created at run-time is not failing at all. Mixing them both seems to be having a problem. Using TIdUDPClient for sending broadcast messages with TIdTCPServer also failing. Again, I need SPECIFIC DETAILS, or I can't help you further. On 1/18/2020 at 2:56 PM, ertank said: In anyway, it seems to be best creating components at run-time as putting them on form at design time is always failing. There should be no difference. Components created at design-time are created dynamically at runtime, just with the parent Form/Frame/DataModule as the Owner. Share this post Link to post