Indy10, Bit messaging between TIdTCPClient and TIdTCPServer. lazarus / 38-Networking and Web Programming ---------------------------------------------------------------------- shyub · 2022-03-04 16:22 The server receives messages from the client, but the client either does not accept or hangs. The sniffer shows that a transmission from the server is in progress. Server: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.Button2Click(Sender: TObject);var  Buf: TIdBytes=nil;begin  SetLength(Buf, 5);  Buf[0]:=$01; Buf[1]:=$10; Buf[2]:=$00; Buf[3]:=$22; Buf[4]:=$33;  MCon.Connection.IOHandler.Write(Buf, 5);end;The client does not receive anything: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.Timer1Timer(Sender: TObject);var AByte: Byte; Buf: TIdBytes=nil; n: Integer; S: String='HEX: ';begin  n:=IdTCPClient1.IOHandler.InputBuffer.Size;  if n>0 then begin    AByte:=IdTCPClient1.IOHandler.ReadByte;    S:=S+IntToHex(AByte, 2)+' ';    IdTCPClient1.IOHandler.ReadBytes(Buf, 4, False);    for n:=0 to Length(Buf)-1 do      S:=S+IntToHex(Buf[n], 2)+' ';  end;end;Client freezes: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.Timer1Timer(Sender: TObject);var Buf: TIdBytes=nil; n: Integer; S: String='HEX: ';begin  if IdTCPClient1.IOHandler.InputBufferIsEmpty then begin    if IdTCPClient1.IOHandler.CheckForDataOnSource(1000) then begin      Timer1.Enabled:=false;      IdTCPClient1.IOHandler.ReadBytes(Buf, 5, False);      for n:=0 to Length(Buf)-1 do        S:=S+IntToHex(Buf[n], 2)+' ';      Timer1.Enabled:=true;    end;  end; end;I am attaching the source code: ---------------------------------------------------------------------- rvk · 2022-03-04 17:12 Quote from: shyub on March 04, 2022, 05:22:00 pmCode: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   if IdTCPClient1.IOHandler.InputBufferIsEmpty then begin    if IdTCPClient1.IOHandler.CheckForDataOnSource(1000) then begin      Timer1.Enabled:=false;      IdTCPClient1.IOHandler.ReadBytes(Buf, 5, False); Just a small question/observation: Why are you continuing to check for data (CheckForDataOnSource) when you just established that the inputbuffer is empty (InputBufferIsEmpty) ? In that case the Timer1Timer will hang on CheckForDataOnSource for 1000ms before it times out. In that time the Timer1 might fire again and check for data again (because you disable the timer in the wrong place). Perhaps you meant something like this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.Timer1Timer(Sender: TObject);var Buf: TIdBytes=nil; n: Integer; S: String='HEX: ';begin  if NOT IdTCPClient1.IOHandler.InputBufferIsEmpty then // NOT will make sure to only do this when there is data  begin    Timer1.Enabled:=false; // directly disable, don't wait for reading of the line, there is already data so disable the timer    try      if IdTCPClient1.IOHandler.CheckForDataOnSource(1000) then      begin      IdTCPClient1.IOHandler.ReadBytes(Buf, 5, False);        for n:=0 to Length(Buf)-1 do          S:=S+IntToHex(Buf[n], 2)+' ';      end;    finally      Timer1.Enabled:=true;    end;  end;end; BTW: I would never do this in a TTimer but in TThread but that is somewhat harder to program and needs some knowledge of threads. ---------------------------------------------------------------------- Remy Lebeau · 2022-03-04 17:43 Quote from: shyub on March 04, 2022, 05:22:00 pmServer: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i MCon.Connection.IOHandler.Write(Buf, 5); That is not really thread-safe.  Where is MCon coming from?  You really should avoid writing any data to a TIdContext from outside of the OnExecute event, especially if the event sends its own data.  You can easily corrupt your communications this way. Quote from: shyub on March 04, 2022, 05:22:00 pmThe client does not receive anything: That is because in that example, you are never attempting to read from the socket, so the InputBuffer.Size is always 0. Quote from: shyub on March 04, 2022, 05:22:00 pmClient freezes: The only problem I see with that example is that CheckForDataOnSource() may receive fewer than 5 bytes, and then ReadBytes() will have to block waiting for the remaining bytes to arrive.  But as long as the server is always sending 5 bytes at a time, that really shouldn't be an issue, unless you are running on a really slow network. Quote from: rvk on March 04, 2022, 06:12:55 pmWhy are you continuing to check for data (CheckForDataOnSource) when you just established that the inputbuffer is empty (InputBufferIsEmpty) ? Because CheckForDataOnSource() performs an actual read of the socket and puts any received bytes into the InputBuffer. Quote from: rvk on March 04, 2022, 06:12:55 pmPerhaps you meant something like this: That is incorrect usage.  I would suggest something more like this instead: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.Timer1Timer(Sender: TObject);var  Buf: TIdBytes=nil;  n: Integer;  S: String;begin  if IdTCPClient1.IOHandler.InputBufferIsEmpty then begin    if not IdTCPClient1.IOHandler.CheckForDataOnSource(0) then begin      IdTCPClient1.IOHandler.CheckForDisconnect;      Exit;    end;  end;  Timer1.Enabled := false;  IdTCPClient1.IOHandler.ReadBytes(Buf, 5, False);  S := 'HEX: ';  for n := 0 to Length(Buf)-1 do    S := S + IntToHex(Buf[n], 2) + ' ';  Timer1.Enabled := true;end; Quote from: rvk on March 04, 2022, 06:12:55 pmBTW: I would never do this in a TTimer but in TThread Agreed.  However, the use of InputBufferIsEmpty() and CheckForDataOnSource() can makes this somewhat safe in the main UI thread, by reducing the time spent waiting for bytes.  Though, the ReadBytes() could still block, which would not be good, but the use of TIdAntiFreeze can mitigate that. ---------------------------------------------------------------------- rvk · 2022-03-04 18:13 Quote from: Remy Lebeau on March 04, 2022, 06:43:35 pmQuote from: rvk on March 04, 2022, 06:12:55 pmWhy are you continuing to check for data (CheckForDataOnSource) when you just established that the inputbuffer is empty (InputBufferIsEmpty) ? Because CheckForDataOnSource() performs an actual read of the socket and puts any received bytes into the InputBuffer. Woops, my bad  I thought InputBufferIsEmpty was more on the raw side to check if there is data on the line (I work mostly with Synapse  ). In that case indeed my code doesn't make sense. ---------------------------------------------------------------------- shyub · 2022-03-04 18:54 Many thanks! And what does "0" mean in "CheckForDataOnSource(0)" and in what cases should "False" or "True" be specified in "ReadBytes(Buf, -1, False)"? In a real program, packets will be from 7 to 50 bytes. I did it like this, it works: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.Timer1Timer(Sender: TObject);var AByte: Byte; Buf: TIdBytes=nil; n: Integer; S: String='HEX: ';begin  if IdTCPClient1.IOHandler.InputBufferIsEmpty then begin // Наличие данных в буфере.    // Перенос байтов из сокета во входной буффер.    if not IdTCPClient1.IOHandler.CheckForDataOnSource(0) then begin      IdTCPClient1.IOHandler.CheckForDisconnect;      Exit;    end;  end;  Timer1.Enabled:=false;   AByte:=IdTCPClient1.IOHandler.ReadByte;  S:=S+IntToHex(AByte, 2)+'_';   IdTCPClient1.IOHandler.ReadBytes(Buf, -1, False);  for n:=0 to Length(Buf)-1 do    S:=S+IntToHex(Buf[n], 2)+' ';  Edit1.Text:=S;  Timer1.Enabled:=true;end; ---------------------------------------------------------------------- Remy Lebeau · 2022-03-07 19:15 Quote from: shyub on March 04, 2022, 07:54:35 pmAnd what does "0" mean in "CheckForDataOnSource(0)" It is a timeout in milliseconds for how long CheckForDataOnSource() waits for any new bytes to be available on the underlying socket.  If any bytes arrive in that timeframe, they are read from the socket and put into the IOHandler.InputBuffer. Quote from: shyub on March 04, 2022, 07:54:35 pmand in what cases should "False" or "True" be specified in "ReadBytes(Buf, -1, False)"? That is the AAppend parameter.  When True, the bytes read are appended to the end of the destination array, preserving any existing bytes already present in the array.  When False, any existing bytes in the array (up to the specified count) are overwritten with the bytes read. Quote from: shyub on March 04, 2022, 07:54:35 pmIn a real program, packets will be from 7 to 50 bytes. I did it like this, it works: What is the purpose of the lead byte?  You are not using it to help you decide how many bytes to read.  You are passing -1 for the AByteCount parameter of ReadBytes(), so it is going to return to you whatever arbitrary bytes happen to be available at that exact moment.  Are you sure that is what you really want?  If the Sender sends 7 bytes, how do you know to read 7 bytes?  If the Sender sends 50 bytes, how do you know to read 50 bytes?  The answer is - you don't, because you didn't design your communication to work that way.