Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
TIdTelnet Disconnect
#1
I'm using telnet client in my app, to access streaming market data.
It works ok for Windows and iOS, but on Android the app hangs
for several seconds when telnet.Disconnect is called.  So much that
Android detects the app is stalled and terminates it.

I understand that Connect and Disconnect can block the
execution, so they are not to be called from the UI thread.
But why Disconnect takes so long ?

One guy suggested on stackoverflow that the reader thread
was locked or not finishing, and that blocked the Disconnect call.

So I tried to create a thread dedicated to disconnect.
It seems to work   Smile 

Code:
procedure TForm1.ThreadedTelnetDisconnect;
var aThreadDisconnect:TThread;
begin
  aThreadDisconnect := TThread.CreateAnonymousThread(
      procedure begin
        IdTelnet1.Disconnect;
      end);
  aThreadDisconnect.start();
end;
Reply
#2
I cant speak for Android, but on Windows and others it is valid to disconnect from a different thread and it is a common practice.
Reply
#3
(11-15-2019, 01:03 PM)omarreis Wrote: I understand that Connect and Disconnect can block the
execution, so they are not to be called from the UI thread.
But why Disconnect takes so long.

Probably because TIdTelnet runs an internal reading thread, and Disconnect() waits for that thread to fully terminate after closing the socket behind the thread's back.

(11-15-2019, 01:03 PM)omarreis Wrote: One guy suggested on stackoverflow that the reader thread
was locked or not finishing, and that blocked the Disconnect call.

Very likely.

(11-15-2019, 01:03 PM)omarreis Wrote: So I tried to create a thread dedicated to disconnect.
It seems to work   Smile 

Rather than doing a full Disconnect() in a worker thread, I would probably just Close() the IOHandler to close the underlying socket, let the reading thread detect the closure (ie, a read failure) and terminate itself, and then have the main thread detect that (or have the worker signal the main thread) to Disconnect() the client to free up the thread.

(11-15-2019, 06:49 PM)kudzu Wrote: I cant speak for Android, but on Windows and others it is valid to disconnect from a different thread and it is a common practice.

Android is Java on top of Linux. On Linux, it is valid to close() a socket in a different thread, provided you shutdown() the socket first (which Indy does) to abort any operations in progress. On non-Windows platforms, simply close()'ing a socket does not guarantee that operations in progress are aborted, especially thread-blocking operations.

It is also not advisable to close sockets across thread boundaries due to file descriptor reuse (which doesn't affect Windows). As soon as the socket is closed, its descriptor can be reused for a completely new file/socket, which can affect threads that were still holding the original descriptor and don't realize the socket is gone. Which, sadly, perfectly describes what TIdTelnet actually does internally. A safer closure design involves using a separate pipe to signal threads to stop their socket operations before the socket is then closed. But Indy does not do that.

Reply
#4
(11-16-2019, 07:54 PM)rlebeau Wrote: Rather than doing a full Disconnect() in a worker thread, I would probably just Close() the IOHandler to close the underlying socket, let the reading thread detect the closure (ie, a read failure) and terminate itself, and then have the main thread detect that (or have the worker signal the main thread) to Disconnect() the client to free up the thread.

I tried: 
  IdTelnet1.IOHandler.Close;
instead of:
  IdTelnet1.Disconnect;

(calls from the UI thread)

That fixed the app blocking problem, but the component no longer called IdTelnet1.Disconnected event, which I use.

The threaded  disconnect solution above seems to work better.

Thanks
Reply
#5
(11-18-2019, 02:18 PM)omarreis Wrote: I tried: 
  IdTelnet1.IOHandler.Close;
instead of:
  IdTelnet1.Disconnect;

(calls from the UI thread)

That fixed the app blocking problem, but the component no longer called IdTelnet1.Disconnected event, which I use.

You shouldn't be using the OnDisconnected event to begin with. Indy clients are not event driven. The OnDisconnected is only called when Disconnect() is called, so you could have (and should have) simply run your OnDisconnected logic right after calling Disconnect().

In any case, if you really need an event, have a look at the OnStatus event, it has an hsDisconnected notification.

Reply


Forum Jump:


Users browsing this thread: 2 Guest(s)