@rturas 0 Posted April 6, 2023 Hello, I am trying to create the app that sends a messages to the server and gets back an answer from it in a thread on the regular time intervals. Request contains 6 bytes, answer should be 65 bytes. My code looks like that: //TThread Constructor inherited Create(True); TCPClient := TIdTCPClient.Create; TCPClient.Host := AHost; TCPClient.Port := APort; TCPClient.ConnectTimeout := 5000; TCPClient.ReuseSocket := rsTrue; //TThread Execute procedure SetLength(wBuffer, 6); //Here i do write some bytes SetLength(rBuffer, 65); while not Terminated do begin try TCPClient.Connect; except on E: Exception do begin TThread.Queue(nil, procedure begin Form2.mmo1.Lines.Add('Exception: ' + e.Message); end); for i := 1 to 5 do begin if not Terminated then Sleep(1000); end; Continue; end; end; Reading := False; i := 1; while not Terminated do begin if not Reading then begin Reading := True; TCPClient.IOHandler.Write(wBuffer, 6, 0); end; try if Reading then begin if TCPClient.IOHandler.CheckForDataOnSource(100) then begin TCPClient.IOHandler.ReadBytes(rBuffer, 65, False); TThread.Queue(nil, procedure begin Form2.mmo1.Lines.Add('Buffer size: ' + IntToStr(TCPClient.IOHandler.InputBuffer.Size)); end); if not TCPClient.IOHandler.InputBufferIsEmpty then begin //I do some stuff here i := i +1; Reading := False; Sleep(1000); end; end; end; except on E: Exception do begin TThread.Queue(nil, procedure begin Form2.mmo1.Lines.Add('Reading exception: ' + e.Message); end); Sleep(1000); Continue; end; end; end; end; I do get only one line in the memo: Buffer size: 0 What am i doing wrong? How can i fix it? Thank you in advance. Share this post Link to post
Remy Lebeau 1398 Posted April 6, 2023 (edited) 5 hours ago, @rturas said: TCPClient.ReuseSocket := rsTrue; There is usually no need to ever use ReuseSocket on a client, unless you use its BoundIP and BoundPort properties to make it connect from a specific local IP/Port pair, AND you are connecting to the same remote IP/Port pair as a previous connection from the same local IP/Port. By default, a client socket connects from a random local port. ReuseSocket is more commonly used only on servers instead. When a server gets shutdown and restarted quickly, ReuseSocket can can allow it to listen on the same local IP/Port as the previous run, in case the OS hasn't released the local IP/Port yet. Quote I do get only one line in the memo: Buffer size: 0 What am i doing wrong? How can i fix it? You are using the InputBuffer the wrong way. After you Write() out your wBuffer, you are waiting in an endless loop until a reply arrives. Why are you using a loop at all? That is not necessary. Indy's reading behavior is designed to block the calling thread waiting for requested data to arrive. Most of the IOHandler's reading methods get their data from the InputBuffer only, not from the socket directly. CheckForDataOnSource() reads directly from the socket and saves whatever it receives into the InputBuffer. So, if you ask a reading method (ie, in this case, ReadBytes()) to read something (ie, in this case, 65 bytes), the method does not exit until all of the bytes for that something are available in the InputBuffer, or until the ReadTimeout elapses (which is infinite by default). In your case, when you do eventually get a reply, you are reading it from the InputBuffer into your rBuffer, but then you are ignoring rBuffer that you just read into and instead you are checking the InputBuffer directly to see if it has any buffered bytes that you have NOT read yet, and only if it DOES then you are flagging yourself to make the next Write() call. But if the InputBuffer is EMPTY (because you have already read everything that was in it) then you are NEVER calling Write() again, and you end up stuck in your reading loop waiting for CheckForDataOnSource() to receive new bytes from the socket which you are NEVER requesting the server to send. You have over-complicated your thread logic. You don't need all of that InputBuffer handling at all. Just call Write(), then ReadBytes() (letting it block), and repeat. That being said, there are some other issues with your code, too. You are not calling Disconnect() after Connect() is successful. You are accessing the InputBuffer in the main threadd while your worker thread may also be accessing it at the same time. And you are not capturing the Exception when calling TThread.Queue(), so the Exception will be destroyed long before the main thread has a chance to access its Message. With all of that said, try this instead: inherited Create(True); TCPClient := TIdTCPClient.Create; TCPClient.Host := AHost; TCPClient.Port := APort; TCPClient.ConnectTimeout := 5000; TCPClient.ReadTimeout := ...; // infinite by default ... // a separate procedure is needed so that TThread.Queue() can // capture and extend the lifetime of the String. See the // documentation for more details: // https://docwiki.embarcadero.com/RADStudio/Sydney/en/Anonymous_Methods_in_Delphi // procedure DisplayMessageInUI(const AMsg: string); begin TThread.Queue(nil, procedure begin Form2.mmo1.Lines.Add(AMsg); end); end; ... SetLength(wBuffer, 6); //write some bytes into wBuffer SetLength(rBuffer, 65); while not Terminated do begin try TCPClient.Connect; except on E: Exception do begin DisplayMessageInUI('Exception: ' + e.Message); for i := 1 to 5 do begin if Terminated then Exit; Sleep(1000); end; Continue; end; end; try try i := 1; while not Terminated do begin TCPClient.IOHandler.Write(wBuffer); TCPClient.IOHandler.ReadBytes(rBuffer, 65, False); // waits for reply // { Alternatively, if you want to keep checking Terminated while waiting: while TCPClient.IOHandler.InputBufferIsEmpty do begin TCPClient.IOHandler.CheckForDataOnSource(100); TCPClient.IOHandler.CheckForDisconnect; if Terminated then Exit; end; TCPClient.IOHandler.ReadBytes(rBuffer, 65, False); } DisplayMessageInUI('Reply received'); //do some stuff with rBuffer Inc(i); Sleep(1000); end; finally TCPClient.Disconnect; end; except on E: Exception do begin DisplayMessageInUI('Exception: ' + e.Message); if Terminated then Exit; Sleep(1000); end; end; end; Edited April 6, 2023 by Remy Lebeau 2 Share this post Link to post
@rturas 0 Posted April 6, 2023 Hello Remy, Thank you so much for the explanation. It is highly appreciated. Share this post Link to post