msd 5 Posted June 13, 2023 Hello friends,  I need some advice about Delphi as a TCP/IP server from you, Delphi developers.🙂 Scenario: I have about 25 computers with permanent connections to the TCP/IP server component. Every client has a dedicated server IP address, which is only one point in this story. Every client sent some HEX code, which I read with ReadByte on the TCP Indy Server, and according to that data, I know the clients locations and IDs. Those clients are LED displays that represent numeric data sent by a TCP/IP server.  Problem: With Indy 10 and Delpi Latest, my app freezes after 1 to 3 hours of use. Only Indy can read those bytes from cache data, and the connection must be permanent.  Q: Is there any VCL on the market that has TCP/IP Server-Client with these features?  P.S. I tried with demo versions of DevArt Secure Bridge and Internet Component Suite, and I couldn't read bytes, only raw text.  Thanks for any advice or help in advance. Share this post Link to post
mvanrijnen 123 Posted June 13, 2023 I'm reasonably sure that you can accomplish this with Indy. You have multiple clients sending data to a single indy server i assume? Maybe you have some memory problems, not correct handling incoming connections and or data?   1 Share this post Link to post
esegece 47 Posted June 13, 2023 Hello, Â I assume that when you say "my app freezes" you are referring to the server. Usually when the server freezes is because there is a thread-lock, Indy servers are multi-threaded but the the event handlers are not thread-safe, so if you need to update a VCL control when you read some data, this operation must be done in the main thread (using Synchronize or Queue). 25 concurrent connections is quite low, so Indy server can handle these connections without problems. If you can provide more info about your code we can try to help you. Â Kind Regards, Sergio 1 Share this post Link to post
msd 5 Posted June 13, 2023 (edited) Hello.  I have one TCP/IP server by the Indy component and one memo field for some kind of monitoring. Indy component accepts connection, sends identification request to client, and opens and keeps openend connection. I'm using three events: OnConnect, OnDisconnect, and, of course, OnExecute. The first two just accept and log connection status and details; everything else is on the OnExecute event. I can represent the function that updates the monitoring memo component.  function TFServer.cliInfoMessage(info: TMemo; send, msg: string): Boolean; begin  TThread.Queue(nil,   procedure   begin    info.Lines.Append('/' + send + '/' + msg);   end); result := true; end;  Another function is the response function, which can be bad coded because it does not generate a response for client activity on an open connection.  P.S. Yes my app is freeze from r time to time and power up CPU to 100% for few minutes.  Thanks again for all...  Edited June 13, 2023 by msd Share this post Link to post
mvanrijnen 123 Posted June 13, 2023 (edited) besides the threadsafety and the question if this is the best solution, probably your memo is getting "full", try to limit the total lines in the memo to for example max. 1000lines, and do the update between beginupdtae and endupdate.  With which intensivity the clients send data )(e,.g. 100 times per second)?   function TFServer.cliInfoMessage(info: TMemo; send, msg: string): Boolean; begin   TThread.Queue(nil,     procedure     begin       info.lines.BeginUpdate();       try        info.Lines.Append('/' + send + '/' + msg);         while info.lines.count>1000 do          info.lines.delete(0);          finally        info.lines.EndUpdate();    end;     end); result := true; end; Edited June 13, 2023 by mvanrijnen 1 Share this post Link to post
msd 5 Posted June 13, 2023 Hello,  In peak time, it can be 5–10 times per second (it is not as heavy as it looks in my description). I have one more method, which is to create a response, and it must be thread-safe. I'll post it here as part of this message. The whole system has two parts: the first is communication with counters, and the other is information about which counter needs to show which number. So, all counters are 100% online with an open connection, and after the system receives new data from another side of the system, it must search all connected counters and pass new data. This is another important part of the code.  function TFServer.cliSendData(cliIP: string; cliCMD: string = ''; cliAddress: string = ''; cliValue: string = ''): Boolean; var  i: integer;  cliList: TList;  byteMsg: TidBytes;  cntClient: TIdContext;   function SetCounter(dspAddress, dspValue: string): TidBytes;  begin   SetLength(bytes, 8);   bytes[0] := BYTE_START;   bytes[1] := BYTE_DATA;   bytes[2] := $31;   bytes[3] := $00;   case StrToInt(Copy(cntValue, 1, 2)) of    00:     bytes[4] := $00;    01:     bytes[4] := $01;    02:     bytes[4] := $02;    03:   end;   bytes[5] := $00;   case StrToInt(cntValue[3]) of    0:     bytes[6] := $00;    1:     bytes[6] := $01;    2:     bytes[6] := $02;   end;   bytes[7] := BYTE_STOP;   result := bytes;  end; begin   byteMsg := SetCounter(cliAddress, cliValue);   try    cliList := DisplayServer.Contexts.LockList;    for i := 0 to cliList.Count - 1 do    begin     cntClient := TIdContext(cliList); // AContext.Connection.Disconnect     if cntClient <> nil then      try       if (cliIP = 'ALL') or (cliIP = cntClient.Binding.PeerIP) then       begin         cntClient.Connection.IOHandler.Write(byteMsg);        cliInfoMessage(dspLog, 'SERVER', cliLogDateTime() + ' - Send: ' + cliCMD + ' to Client: - ' + cntClient.Binding.PeerIP);       end;      except      end;    end;   finally    DisplayServer.Contexts.UnlockList;   end;  result := true; end;  Thanks to all developers for their help and assistance. Share this post Link to post
Fr0sT.Brutal 900 Posted June 14, 2023 (edited) 17 hours ago, mvanrijnen said: probably your memo is getting "full", try to limit the total lines in the memo to for example max. 1000lines, and do the update between beginupdtae and endupdate. This will slowdown the app because of Memo performance, I use Selection for this if mLog.Lines.Count > 2000 then begin mLog.Lines.BeginUpdate; // Deleting lines one by one is damn slow. // So we take length of text and cut half of it mLog.SelStart := 0; mLog.SelLength := mLog.GetTextLen div 2; mLog.SelText := ''; // Remove probably partial line mLog.Lines.Delete(0); mLog.Lines.EndUpdate; end;  11 hours ago, msd said: cntClient := TIdContext(cliList); This looks suspicious, shouldn't it be cliList [ i ]? Edited June 14, 2023 by Fr0sT.Brutal Share this post Link to post