Recent

Author Topic: [SOLVED] Indy: check if client is effectively connected  (Read 2184 times)

torbente

  • Sr. Member
  • ****
  • Posts: 325
    • Noso Main Page
[SOLVED] Indy: check if client is effectively connected
« on: February 28, 2021, 08:22:41 pm »
Hi everyone
Using Lazarus 2.0.10 on windows 8.1

I use the following procedure to periodically check if the client receives a line from the server:

Code: Pascal  [Select][+][-]
  1. Procedure ReadClientLines();
  2. var
  3.   linea : string;
  4. Begin
  5. if not ClientChannel.Connected then exit;
  6. try
  7.    if ClientChannel.IOHandler.InputBufferIsEmpty then
  8.       begin
  9.       ClientChannel.IOHandler.CheckForDataOnSource(10);
  10.       if ClientChannel.IOHandler.InputBufferIsEmpty then Exit;
  11.       end;
  12.    While not ClientChannel.IOHandler.InputBufferIsEmpty do
  13.       begin
  14.       Linea := ClientChannel.IOHandler.ReadLn(IndyTextEncoding_UTF8);  
  15.       ....
  16.       end;
  17. Except on E:Exception do
  18.    ReportTheError(E.Message);
  19. End;

But if the internet connection just ends (i turn off the wifi connection to test), the application hangs in this procedure.

How i could prevent this?
« Last Edit: April 10, 2021, 11:23:05 pm by torbente »
Noso Cryptocurrency Main Developer
https://github.com/DevTeamNoso/NosoWallet

krolikbest

  • Full Member
  • ***
  • Posts: 246
Re: Indy: check if client is effectively connected
« Reply #1 on: March 01, 2021, 08:03:16 am »
e.g. use ping in separate thread

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Indy: check if client is effectively connected
« Reply #2 on: March 01, 2021, 05:21:26 pm »
I don't know where it hangs in your code, but I'll assume inside the while loop. Simply use the other form of ReadLn that has a timeout parameter.

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1311
    • Lebeau Software
Re: Indy: check if client is effectively connected
« Reply #3 on: March 01, 2021, 08:02:14 pm »
Simply use the other form of ReadLn that has a timeout parameter.

Or, you can set the IOHandler's ReadTimeout property beforehand. And check the IOHandler's ReadLnTimedOut property after calling ReadLn().
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1311
    • Lebeau Software
Re: Indy: check if client is effectively connected
« Reply #4 on: March 01, 2021, 08:11:22 pm »
Code: Pascal  [Select][+][-]
  1. if not ClientChannel.Connected then exit;
  2. try
  3.    if ClientChannel.IOHandler.InputBufferIsEmpty then
  4.       begin
  5.       ClientChannel.IOHandler.CheckForDataOnSource(10);

Note that Connected() performs a read operation internally (with a 0-second timeout), so calling CheckForDataOnSource() immediately afterwards is a bit redundant.  The InputBuffer will either be empty or not after Connected() returns.

But if the internet connection just ends (i turn off the wifi connection to test), the application hangs in this procedure.

Makes sense, since an abnormal loss of connection is not reported by the OS in a timely manner (it can take minutes/hours!), so until the OS eventually times out internally and invalidates the connection, Connected() will happily continue to return True, and ReadLn() will happily wait for new data up to the specified timeout (via its optional ATimeout parameter, or the IOHandler's ReadTimeout property - neither of which you appear to be using).

How i could prevent this?

Just because Connected()/CheckForDataOnSource() report that data has arrived does not guarantee that a complete line is available in the InputBuffer.  You should assign a non-infinite timeout to the ReadLn() operation itself.

You might also consider enabling TCP-level keepalives (via ClientChannel.Socket.Binding.SetKeepAliveValues(), for instance) so that the OS can timeout internally sooner rather than later if the connection is lost unexpectedly.
« Last Edit: March 01, 2021, 08:16:15 pm by Remy Lebeau »
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

torbente

  • Sr. Member
  • ****
  • Posts: 325
    • Noso Main Page
Re: Indy: check if client is effectively connected
« Reply #5 on: March 16, 2021, 12:38:50 am »
Ok, trying to improve the connection.

I now included a variable:

Code: Pascal  [Select][+][-]
  1. ReadTimeOutTime : integer; //set to 50

Code: Pascal  [Select][+][-]
  1. Procedure ReadClientLines();
  2. var
  3.   linea : string;
  4. Begin
  5. if not ClientChannel.Connected then exit;
  6. try
  7.    if ClientChannel.IOHandler.InputBufferIsEmpty then
  8.       begin
  9.       ClientChannel.IOHandler.CheckForDataOnSource(10);
  10.       if ClientChannel.IOHandler.InputBufferIsEmpty then Exit;
  11.       end;
  12.    While not ClientChannel.IOHandler.InputBufferIsEmpty do
  13.       begin
  14.       ClientChannel.ReadTimeout:=ReadTimeOutTime ;
  15.       Linea := ClientChannel.IOHandler.ReadLn(IndyTextEncoding_UTF8);  
  16.      
  17.       ....
  18.       end;
  19. Except on E:Exception do
  20.    ReportTheError(E.Message);
  21. End;

This helps to keep connection with slow peers, but how i could detect if the readtimeout expired? How is...

Quote
Or, you can set the IOHandler's ReadTimeout property beforehand. And check the IOHandler's ReadLnTimedOut property after calling ReadLn().

... this implemented?

Quote
You might also consider enabling TCP-level keepalives (via ClientChannel.Socket.Binding.SetKeepAliveValues(), for instance) so that the OS can timeout internally sooner rather than later if the connection is lost unexpectedly.

The system includes a ping system every 5 seconds to keep connections data updated.
Noso Cryptocurrency Main Developer
https://github.com/DevTeamNoso/NosoWallet

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1311
    • Lebeau Software
Re: Indy: check if client is effectively connected
« Reply #6 on: March 16, 2021, 03:43:12 am »
I now included a variable:

Code: Pascal  [Select][+][-]
  1. ReadTimeOutTime : integer; //set to 50

FYI, you can pass that variable directly to ReadLn(), you don't have to reset the ReadTimeout property every time:

Code: Pascal  [Select][+][-]
  1. Procedure ReadClientLines();
  2. var
  3.   linea : string;
  4. Begin
  5.   if not ClientChannel.Connected then Exit;
  6.   try
  7.     if ClientChannel.IOHandler.InputBufferIsEmpty then
  8.     begin
  9.       ClientChannel.IOHandler.CheckForDataOnSource(10);
  10.       if ClientChannel.IOHandler.InputBufferIsEmpty then Exit;
  11.     end;
  12.     repeat
  13.       // consider setting the IOHandler.DefStringEncoding beforehand, such as
  14.       // at connection open.  No need to pass it in to ReadLn() every time...
  15.       //
  16.       // ClientChannel.ReadTimeout := ReadTimeOutTime;
  17.       Linea := ClientChannel.IOHandler.ReadLn('', ReadTimeOutTime, -1, IndyTextEncoding_UTF8);
  18.       if ClientChannel.IOHandler.ReadLnTimedOut then Exit;
  19.       // use Linea as needed...
  20.     until ClientChannel.IOHandler.InputBufferIsEmpty;
  21.   except
  22.     on E: Exception do
  23.       ReportTheError(E.Message);
  24.   end;
  25. end;

This helps to keep connection with slow peers, but how i could detect if the readtimeout expired?

By checking the IOHandler.ReadLnTimedOut property, like I said earlier, and demonstrated above.
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

torbente

  • Sr. Member
  • ****
  • Posts: 325
    • Noso Main Page
Re: Indy: check if client is effectively connected
« Reply #7 on: April 10, 2021, 11:22:34 pm »
I fixed all issues and now the app is running for days without hang  :D

I will open a new thread for a different answer.
Noso Cryptocurrency Main Developer
https://github.com/DevTeamNoso/NosoWallet

 

TinyPortal © 2005-2018