JusJJ 0 Posted September 9 I have a reconnection problem in an android application,using Indy TidTcpClient and TIdThreadComponent. I've tried both the Delphi bundled and the latest Indy version. At the beginning, the program goes through the IP addresses of the available devices. Read and write routines work without problems, but when you try to change the IP address, almost without exception the program freezes. Sometimes it takes a few seconds, sometimes a few minutes before the program responds and about every ten times the program stops responding completely. In Windows, changing the IP address works reasonably well, but sometimes it also freezes. Receiving works...: try StartChar := TCPSocket.IOHandler.ReadChar; if StartChar = '%' then begin Data := TCPSocket.IOHandler.ReadLn('&'); Payload := Data; TCPSocket.IOHandler.ReadBytes(ChecksumBytes, 4); ReceivedChecksum := BytesToString(ChecksumBytes); CalculatedChecksum := CRC16(Payload); if ReceivedChecksum = CalculatedChecksum then begin TThread.Queue(nil, procedure var i: Integer; begin HandleData; end); end else begin TThread.Queue(nil, procedure begin HandleChecksumError; end); end; end; except on E: Exception do begin TThread.Queue(nil, procedure begin HandleException; end); ReceiveThread.Terminate; end; ...but changing IP fails: procedure IpSelectF.IPBtnClick(Sender: TObject); Var Btn: TButton; NewIp: string; begin if Sender is TButton then begin Btn := TButton(Sender); if AvailableIP[Btn.Tag] then begin TTask.Run( procedure begin try if Assigned(MainF.ReceiveThread) and MainF.ReceiveThread.Active then begin MainF.ReceiveThread.Stop; MainF.ReceiveThread.WaitFor; end; if MainF.TCPSocket.Connected then MainF.TCPSocket.Disconnect; NewIp := '192.168.1.' + IntToStr(Btn.Tag); MainF.TCPSocket.Host := NewIp; try MainF.TCPSocket.ConnectTimeout := 3000; MainF.TCPSocket.Connect; if MainF.TCPSocket.Connected then begin TThread.Queue(nil, procedure begin MainF.ReceiveThread.Start; ShowAlertDialog('Connected to ' + NewIp); end ); end else begin TThread.Queue(nil, procedure begin ShowAlertDialog('Connection to ' + NewIp + ' failed.'); end ); end; except on E: Exception do begin TThread.Queue(nil, procedure begin ShowAlertDialog('Failed to connect to ' + NewIp + ': ' + E.Message); end ); end; end; except on E: Exception do begin TThread.Queue(nil, procedure begin ShowAlertDialog('Error during IP switch: ' + E.Message); end ); end; end; end ); end; end; Share this post Link to post
Remy Lebeau 1436 Posted September 9 The first thing I notice is that you are calling Connected() across thread boundaries, Don't do that. Connected() performs a socket read in the calling thread, so it could interfere with a read operation in another thread. You shouldn't be calling Connected() at all. Certainly not right after a successful Connect() (which will raise an exception if it fails), and not right before a Disconnect() (which will close the socket and doesn't care if the socket was connected). Another thing I notice is that you are not serializing access to your processing data. TThread.Queue() runs asynchronously, so it is possible that you might receive a new message and overwrite your variables before an earlier queued HandleData() call has been processed. I also wonder why you are creating a new Task to stop the reading thread and reconnect, but are using the main thread to start the reading thread? Your use of multiple threads is odd to me. I would suggest a different design. I would move the (re)connect logic into the reading thread itself, where the thread runs a loop that connects, reads until done, and then disconnects, repeating until stopped. If the IP needs to change, then cache the new IP and ask the thread to reconnect at its earliest convenience. That way, the thread alone decides when it is safe to read and when it is safe to disconnect and reconnect, notifying the main thread of any status changes as needed. Try something more along the lines of this: // set ReceiveThread.Loop = True // start the thread when you are ready to connect the 1st time // stop the thread when you are ready for the final disconnect public ServerHost: String; ServerHostChanged: Boolean; procedure TMainForm.ReceiveThreadRun(Sender: TIdThreadComponent); begin try if (TCPSocket.Host = '') or ServerHostChanged then begin TCPSocket.Host := ServerHost; ServerHostChanged := False; end; try TCPSocket.Connect; except on E: Exception do begin ... for var I := 1 to 5 do begin if not ReceiveThread.Active then Break; Sleep(1000); end; Exit; end; end; try ... while ReceiveThread.Active and (not ServerHostChanged) do begin try if TCPSocket.IOHandler.InputBufferIsEmpty then begin TCPSocket.IOHandler.CheckForDataOnSource(250); TCPSocket.IOHandler.CheckForDisconnect; if TCPSocket.IOHandler.InputBufferIsEmpty then Continue; end; // read a message and process as needed ... except ... Break; end; end; finally TCPSocket.Disconnect; ... end; except ... end; end; ... procedure IpSelectF.IPBtnClick(Sender: TObject); var Btn: TButton; begin Btn := Sender as TButton; if not AvailableIP[Btn.Tag] then Exit; MainF.ServerHost := '192.168.1.' + IntToStr(Btn.Tag); MainF.ServerHostChanged := True; if not MainF.ReceiveThread.Active then MainF.ReceiveThread.Start; end; Share this post Link to post
JusJJ 0 Posted September 10 (edited) Thank you very much Remy! It seems to work that way. One more question : What would be the best and fastest way to initially check if the devices are connected between 192.168.1.1 - 192.168.1.42? Edited September 10 by JusJJ Share this post Link to post
Remy Lebeau 1436 Posted September 10 2 hours ago, JusJJ said: What would be the best and fastest way to initially check if the devices are connected between 192.168.1.1 - 192.168.1.42? You are going to have to elaborate on that. What are you trying to check, exactly? Share this post Link to post
JusJJ 0 Posted September 10 It is an old, huge industrial compressed air system. It is divided into sectors with a total of 42 devices, valves, dryers, coolers etc. Some devices automatically go into sleep mode because whole system can only work at full power momentarily. Each device is controlled via an ethernet converter and, since it draws the operating voltage directly from the actuator in question, it is of course not possible to contact it. Now, if you need to adjust an individual device to change settings in some sector, it's easier to see directly which devices are in use than to go through them one by one. Share this post Link to post
Remy Lebeau 1436 Posted September 10 I still don't understand what you are asking for, exactly. Share this post Link to post
JusJJ 0 Posted September 10 How to go through ip addresses between 192.168.1.1-192.168.1.42 are they available. If device[1].canconnect then deviceavailable[1] = true else deviceavailable[1] = false etc. Share this post Link to post
Remy Lebeau 1436 Posted September 11 If you are asking how to know whether a given device can be connected to by your TCP client, then the only way to determine that is to actually attempt to connect to the device's TCP server and see if it succeeds or fails. At the very least, you might try simply pinging each IP to see if it is on the network, but that won't tell you whether the TCP server can be connected to. If you have access to change the device's code, then you might add a UDP server that runs alongside the TCP server, and then you can send a UDP broadcast across the network subnet and see which UDP servers respond. Share this post Link to post