Indy10 question lazarus / 38-Networking and Web Programming ---------------------------------------------------------------------- CraigNZ · 2019-11-01 23:31 I have setup PortPeeker as a TCP/IP server to send 'Hello' when it gets a connect request and to echo any subsequent messages.  I have dropped a IdTCPClient component and a IdIOHandlerStack component on the form and then linked the IOHandler to the TCPClient.  After a bit of testing I found it was not necessary to execute a TCPClient.connect as the stack.open seems to do it. Here is the test code I am using:       try         Main_Form.IdIOHandlerStack.Open;         s1 := Main_Form.IdIOHandlerStack.ReadLn();         Main_Form.IdIOHandlerStack.WriteLn('Hello World');         s2 := Main_Form.IdIOHandlerStack.ReadLn();         Main_Form.IdIOHandlerStack.Close;       finally         ShowMessage('received: ' + s1 + ',' + s2);       end; When I execute this code the server does indeed get a connect request from the .Open statement and sends back a 'Hello'.  The next ReadLn executes correctly and s1 now has 'Hello'.  I then send a 'Hello World' to the server (the writeln includes CR LF) and indeed the server does receive it with CR LF and then echoes it back.  The next line as a read timeout of 2000 (2 seconds) but is triggered instantly but S2 is empty.  The ShowMessage shows s1 and s2 but s2 is empty.  The server does indicate a recv problem as if the client failed to receive the echo'd string 'Hello World'. Any idea why this first readln works but not the second one? Craig ---------------------------------------------------------------------- CraigNZ · 2019-11-02 00:11 I have also tried removing the IOHandlerstack and doing this instead:   try     Main_Form.IdTCPClient.Connect;     s1 := Main_Form.IdTCPClient.IOHandler.ReadLn();     Main_Form.IdTCPClient.IOHandler.WriteLn('Hello World');     s2 := Main_Form.IdTCPClient.IOHandler.ReadLn();   finally     ShowMessage('received: ' + s1 + ',' + s2);   end; but I get the same results, s2 shows a null string. Craig ---------------------------------------------------------------------- CraigNZ · 2019-11-02 02:31 For some reason it is now working.  I made no changes to the code but I did increase the ReadTimeout property from 2000 to 20000 .. that worked.  Then I successively reduced the value and tested at several smaller values, all worked.  Finally ended up back at 2000 and it still worked.  So not sure what happened but everything seems okay now. ---------------------------------------------------------------------- Remy Lebeau · 2019-11-05 19:28 Quote from: CraigNZ on November 02, 2019, 12:31:18 amI have dropped a IdTCPClient component and a IdIOHandlerStack component on the form and then linked the IOHandler to the TCPClient. You don't need to link a TIdIOHandlerStack manually unless you are using a proxy.  TIdTCPClient.Connect() will create a default TIdIOHandlerStack for you if no IOHandler is assigned yet. Quote from: CraigNZ on November 02, 2019, 12:31:18 amAfter a bit of testing I found it was not necessary to execute a TCPClient.connect as the stack.open seems to do it. TIdTCPClient.Connect() calls TIdIOHandler.Open() internally to make the actual socket connection.  But there is a lot more work that TIdTCPClient.Connect() does before and after calling TIdIOHandler.Open(), so it is not enough to just call TIdIOHandler.Open() directly.  So just don't do it.  Use TIdTCPClient.Connect() instead (and TIdTCPClient.Disconnect() instead of TIdIOHandler.Close()). Code: [Select]try   Main_Form.IdTCPClient.Connect;   try     s1 := Main_Form.IdTCPClient.IOHandler.ReadLn();     Main_Form.IdTCPClient.IOHandler.WriteLn('Hello World');     s2 := Main_Form.IdTCPClient.IOHandler.ReadLn();   finally     Main_Form.IdTCPClient.Disconnect;   end; finally   ShowMessage('received: ' + s1 + ',' + s2); end; Quote from: CraigNZ on November 02, 2019, 12:31:18 amAny idea why this first readln works but not the second one? Not without seeing what your server code actually looks like. Quote from: CraigNZ on November 02, 2019, 01:11:31 amI have also tried removing the IOHandlerstack and doing this instead: ... but I get the same results, s2 shows a null string. Assuming your server is not actually doing something wrong (which we can't see), another possibility is just typical network lag lasting longer than your ReadTimeout was prepared to allow. Quote from: CraigNZ on November 02, 2019, 03:31:51 amFor some reason it is now working.  I made no changes to the code but I did increase the ReadTimeout property from 2000 to 20000 .. that worked.  Then I successively reduced the value and tested at several smaller values, all worked.  Finally ended up back at 2000 and it still worked.  So not sure what happened but everything seems okay now. Indy's ReadTimeout is essentially a per-byte timeout.  Indy buffers inbound data for you, the ReadTimeout applies when Indy has to go back to the socket connection to read more bytes into its buffer.  So the ReadTimeout means not even 1 new byte arrived during that time period. So, that means in your initial run, something was delaying/blocking bytes from reaching your client for more than 2 seconds, so TIdIOHandler.ReadLn() gave up and exited (setting the TIdIOHandler.ReadLnTimedOut property to true).  Since you did not get an actual read error, chances are had you called TIdIOHandler.ReadLn() again, it likely would have finished reading the remaining bytes and returned the full line you were expecting. Unlike most of TIdIOHandler's other reading methods, TIdIOHandler.ReadLn() does not raise an exception on timeout, it sets the TIdIOHandler.ReadLnTimedOut property instead, which you have to check manually. FYI, Indy's default ReadTimeout is infinite, so you must have manually set it to 2 seconds initially.  I really don't recommend using a timeout value less than approx 10-15 seconds for network traffic, you never know when network hiccups/delays can occur outside of your control. Same with the TIdTCPClient.ConnectTimeout property.  It is also infinite by default (unless TIdAntiFreeze is used, then the default is 2 minutes instead).  If you set a manual ConnectTimeout, I do not recommend going below approx 5-10 seconds.