title: Delphi TCPClient read string from TCPServer
body: <p>I need to write a simple chat program that will be used by some customers. Basically, there are a lot of clients connected to a server and they chat together. The server works:</p>
  
  <p><a href="https://i.stack.imgur.com/Urul2.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/Urul2.png" alt="enter image description here"></a></p>
  
  <p>Here the code if needed:</p>
  
  <pre><code>//CONNECT TO THE SERVER
  procedure TFormServer.ButtonStartClick(Sender: TObject);
  begin
    if not TCPServer.Active then
      begin
        try
          TCPServer.DefaultPort := 8002;
          TCPServer.Bindings[0].IP := LIP.Text;
          TCPServer.Bindings[0].Port := StrToInt(LPort.Text);
          TCPServer.MaxConnections := 5;
          TCPServer.Active := true;
  
          Memo1.Lines.Add(TimeNow + 'Server started.');
        except
          on E: Exception do
            Memo1.Lines.Add(sLineBreak + ' ====== INTERNAL ERROR ====== ' +
              sLineBreak + ' &gt; ' + E.Message + sLineBreak);
        end;
      end;
  end;
  
  //DISCONNECT
  procedure TFormServer.ButtonStopClick(Sender: TObject);
  begin
    if TCPServer.Active then
      begin
        TCPServer.Active := false;
        Memo1.Lines.Add(TimeNow + 'Server stopped.');
      end;
  end;
  
  //IF CLOSE THE APP DONT FORGET TO CLOSE SERVER!!
  procedure TFormServer.FormClose(Sender: TObject; var Action: TCloseAction);
  begin
    ButtonStopClick(Self);
  end;
  
  procedure TFormServer.FormCreate(Sender: TObject);
  begin
    FClients := 0;
  end;
  
  //When a client connects I write a log
  procedure TFormServer.TCPServerConnect(AContext: TIdContext);
  begin
    Inc(FClients);
    TThread.Synchronize(nil, procedure
                             begin
                               LabelCount.Text := 'Connected sockets: ' + FClients.ToString;
                               Memo1.Lines.Add(TimeNow + ' Client connected @ ' + AContext.Binding.IP + ':' + AContext.Binding.Port.ToString);
                             end);
  end;
  
  //Same, when a client disconnects I log it
  procedure TFormServer.TCPServerDisconnect(AContext: TIdContext);
  begin
    Dec(FClients);
    TThread.Synchronize(nil, procedure
                             begin
                               LabelCount.Text := 'Connected sockets: ' + FClients.ToString;
                               Memo1.Lines.Add(TimeNow + ' Client disconnected');
                             end);
  end;
  
  //WHAT I DO HERE:
  //I receive a message from the client and then I send this message to EVERYONE that is connected here. It is a global chat
  procedure TFormServer.TCPServerExecute(AContext: TIdContext);
  var
    txt: string;
  begin
    txt := AContext.Connection.IOHandler.ReadLn();
    AContext.Connection.IOHandler.WriteLn(txt);
    TThread.Synchronize(nil, procedure
                             begin
                               Memo1.Lines.Add(TimeNow + txt);
                             end);
  end;
  </code></pre>
  
  <p>The sever code is very easy and minimal but it does what I need. This is the client instead:</p>
  
  <p><a href="https://i.stack.imgur.com/mLMjz.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/mLMjz.png" alt="enter image description here"></a></p>
  
  <p>Here there is the code, very simple:</p>
  
  <pre><code>//CONNECT TO THE SERVER
  procedure TFormClient.ConnectClick(Sender: TObject);
  begin
  
    if Length(Username.Text) &lt; 4 then
      begin
        Memo1.Lines.Clear;
        Memo1.Lines.Add('ERROR: Username must contain at least 4 characters');
        Exit;
      end;
  
    if not TCPClient.Connected then
      begin
        try
          Username.Enabled := false;
          Memo1.Lines.Clear;
  
          TCPClient.Host := '127.0.0.1';
          TCPClient.Port := 8002;
          TCPClient.ConnectTimeout := 5000;
          TCPClient.Connect;
  
          Connect.Text := 'Disconnect';
        except
          on E: Exception do
            Memo1.Lines.Add(' ====== ERROR ======' + sLineBreak +
              ' &gt; ' + E.Message + sLineBreak);
        end;
      end
    else
      begin
        TCPClient.Disconnect;
        Username.Enabled := true;
        Connect.Text := 'Connect';
      end;
  end;
  
  //IF YOU FORGET TO DISCONNECT WHEN APP IS CLOSED
  procedure TFormClient.FormDestroy(Sender: TObject);
  begin
    if TCPClient.Connected then
      TCPClient.Disconnect;
  end;
  
  //Here I send a string to the server and it's good
  procedure TFormClient.SendClick(Sender: TObject);
  begin
    if TCPClient.Connected then
      begin
        TCPClient.IOHandler.WriteLn(Username.Text + ': ' + EditMessage.Text);
        EditMessage.Text := '';
      end
    else
      begin
        Memo1.Lines.Add('ERROR: You aren''t connected!');
      end;
  end;
  
  //Problems here
  procedure TFormClient.Timer1Timer(Sender: TObject);
  begin
    Memo1.Lines.Add(TCPClient.IOHandler.ReadLn());
  end;
  </code></pre>
  
  <hr>
  
  <p>The problems start in the last procedure <code>Timer1Timer</code>. I have found that TCPServer uses a thread and this is why I call <code>Synchronize</code> to update the UI. Instead TCPClient does not use a thread and I manually have to check the server. Please see this code:</p>
  
  <pre><code>    procedure TFormServer.TCPServerExecute(AContext: TIdContext);
      var
        txt: string;
      begin
        txt := AContext.Connection.IOHandler.ReadLn();
        AContext.Connection.IOHandler.WriteLn(txt);
        TThread.Synchronize(nil, procedure
                                 begin
                                   Memo1.Lines.Add(TimeNow + txt);
                                 end);
      end;
  </code></pre>
  
  <p>As you can see, when the server receives a string he immediatly sends it back to all the clients. I try to get the string here:</p>
  
  <pre><code>procedure TFormClient.Timer1Timer(Sender: TObject);
  begin
    Memo1.Lines.Add(TCPClient.IOHandler.ReadLn());
  end;
  </code></pre>
  
  <p>What is wrong? I have seen a similar question here and answers said that I have to use a timer and <code>IOHandler.ReadLn()</code>, which is what I am doing. I think that the problem is here. How to fix?</p>
  
  <p>Also timer has an interval of 200, is it too short?</p>
  
  <hr>
  
  <p>I have read what Remy Lebeau said in the answer and I've produced this simple code:</p>
  
  <pre><code>procedure TFormClient.Timer1Timer(Sender: TObject);
  begin
    if not(TCPClient.Connected) then
      Exit;
    if TCPClient.IOHandler.InputBufferIsEmpty then
      Exit;
    Memo1.Lines.Add(TCPClient.IOHandler.InputBufferAsString());
  end;
  </code></pre>
  
  <p>There is a <code>Timer1</code> component in the form. This works as I expect but it still could lock the UI or not?</p>