Lazarus

Programming => Networking and Web Programming => Topic started by: torbente on February 28, 2021, 08:22:41 pm

Title: [SOLVED] Indy: check if client is effectively connected
Post by: torbente 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?
Title: Re: Indy: check if client is effectively connected
Post by: krolikbest on March 01, 2021, 08:03:16 am
e.g. use ping in separate thread
Title: Re: Indy: check if client is effectively connected
Post by: engkin 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.
Title: Re: Indy: check if client is effectively connected
Post by: Remy Lebeau 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().
Title: Re: Indy: check if client is effectively connected
Post by: Remy Lebeau 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.
Title: Re: Indy: check if client is effectively connected
Post by: torbente 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.
Title: Re: Indy: check if client is effectively connected
Post by: Remy Lebeau 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.
Title: Re: Indy: check if client is effectively connected
Post by: torbente 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.
TinyPortal © 2005-2018