Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 7596089
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 30, 20262026-05-30T21:50:04+00:00 2026-05-30T21:50:04+00:00

I am quite new to Indy, have implemented very basic idTCPServer and IdTCPClient between

  • 0

I am quite new to Indy, have implemented very basic idTCPServer and IdTCPClient between 2 pc’s on local LAN (no internet), but now need 3 machines to talk!
(Environment is Delphi 2010/Win7/DBExpress/MySQL).
Application is to control a real-time sailing event. A “master” pc and a “slave” (now need 2 slaves!).
The slave pc’s enable sailors to register their details for the event (stored into MySQL tables). The master pc controls a) when registration screen opens/closes, b) sends event details to slaves, c) send real-time race countdown/start and race elapsed times to slaves which they must display and react to (closing registration screen etc). Master needs to know when new person has signed in or out to update it’s racelist.

Currently I implemented (my first Indy program) with IDTCPServer on master. IdTCPClient on slave tells master when new SignIn/Out, and continually sends “time request” text msgs to server, as I dont know how to send message from a TCPServer!).

This I think is not best way to do it, and now the club wants TWO “Sign On” slaves I need to re-visit whole thing, so I ask your advice please…

Which Indy components are best to use? Is it best to have TCPServer on “master” pc? Should the server broadcast to the 2 slaves? and (please!) is there any example that has similar functionality that I can use as a base to help me implement?
Many thanks
Chris

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-30T21:50:05+00:00Added an answer on May 30, 2026 at 9:50 pm

    Using TIdTCPServer on the master and TIdTCPClient on the slaves is the right way to go.

    One way to send messages from the server to clients is to use the server’s Threads property (Indy 9 and earlier) or Contexts property (Indy 10) to access the list of currently connected clients. Each client has a TIdTCPConnection object associated with it for communicating with that client. When needed, you can lock the server’s client list, loop through it writing your message to each client, and then unlock the list:

    Indy 9:

    procedure TMaster.SendMsg(const S: String);
    var
      List: TList;
      I: Integer;
    begin
      List := IdTCPServer1.Threads.LockList;
      try
        for I := 0 to List.Count-1 do
        begin
          try
            TIdPeerThread(List[I]).Connection.WriteLn(S);
          except
          end;
        end;
      finally
        IdTCPServer1.Threads.UnlockList;
      end;
    end;
    

    Indy 10:

    procedure TMaster.SendMsg(const S: String);
    var
      List: TList;
      I: Integer;
    begin
      List := IdTCPServer1.Contexts.LockList;
      try
        for I := 0 to List.Coun-1 do
        begin
          try
            TIdContext(List[I]).Connection.IOHandler.WriteLn(S);
          except
          end;
        end;
      finally
        IdTCPServer1.Contexts.UnlockList;
      end;
    end;
    

    This has some disadvantages, though.

    One disadvanage is that messages are serialized so if one client freezes up, subsequent clients will not receive their messages in a timely manner. Another issue is that clients run in their own threads on the server, so when sending data to a client from multiple threads at the same time, you would have to provide your own per-connection locking mechanism (such as a critical section or mutex) around every write access to a connection to avoid overlapping data and corrupting your communications

    To avoid those pitfalls, it is better to give each client an outbound queue of messages, and then let the server’s OnExecute even send the queued messages on its own schedule. That way, multiple clients can receive messages in parallel instead of serially:

    Indy 9:

    uses
      ..., IdThreadSafe;
    
    procedure TMaster.IdTCPServer1Connect(AThread: TIdPeerThead);
    begin
      AThread.Data := TIdThreadSafeStringList.Create;
    end;
    
    procedure TMaster.IdTCPServer1Disconnect(AThread: TIdPeerThead);
    begin
      AThread.Data.Free;
      AThread.Data := nil;
    end;
    
    procedure TMaster.IdTCPServer1Execute(AThread: TIdPeerThead);
    var
      Queue: TIdThreadSafeStringList;
      List: TStringList;
      Tmp: TStringList;
      I: Integer;
    begin
      ...
      Queue := TIdThreadSafeStringList(AThread.Data);
      List := Queue.Lock;
      try
        if List.Count > 0 then
        begin
          Tmp := TStringList.Create;
          try
            Tmp.Assign(List);
            List.Clear;
          except
            Tmp.Free;
            raise;
          end;
        end;
      finally
        Queue.Unlock;
      end;
      if Tmp <> nil then
      try
        AThread.Connection.WriteStrings(Tmp, False);
      finally
        Tmp.Free;
      end;
      ...
    end;
    
    procedure TMaster.SendMsg(const S: String);
    var
      List: TList;
      I: Integer;
    begin
      List := IdTCPServer1.Threads.LockList;
      try
        for I := 0 to List.Count-1 do
        begin
          try
            TIdThreadSafeStringList(TIdPeerThread(List[I]).Data).Add(S);
          except
          end;
        end;
      finally
        IdTCPServer1.Threads.UnlockList;
      end;
    end;
    

    Indy 10:

    uses
      ..., IdThreadSafe;
    
    procedure TMaster.IdTCPServer1Connect(AContext: TIdContext);
    begin
      AContext.Data := TIdThreadSafeStringList.Create;
    end;
    
    procedure TMaster.IdTCPServer1Disconnect(AContext: TIdContext);
    begin
      AContext.Data.Free;
      AContext.Data := nil;
    end;
    
    procedure TMaster.IdTCPServer1Execute(AContext: TIdContext);
    var
      Queue: TIdThreadSafeStringList;
      List: TStringList;
      Tmp: TStringList;
      I: Integer;
    begin
      ...
      Queue := TIdThreadSafeStringList(AContext.Data);
      List := Queue.Lock;
      try
        if List.Count > 0 then
        begin
          Tmp := TStringList.Create;
          try
            Tmp.Assign(List);
            List.Clear;
          except
            Tmp.Free;
            raise;
          end;
        end;
      finally
        Queue.Unlock;
      end;
      if Tmp <> nil then
      try
        AContext.Connection.IOHandler.Write(Tmp, False);
      finally
        Tmp.Free;
      end;
      ...
    end;
    
    procedure TMaster.SendMsg(const S: String);
    var
      List: TList;
      I: Integer;
    begin
      List := IdTCPServer1.Contexts.LockList;
      try
        for I := 0 to List.Count-1 do
        begin
          try
            TIdThreadSafeStringList(TIdContext(List[I]).Data).Add(S);
          except
          end;
        end;
      finally
        IdTCPServer1.Contexts.UnlockList;
      end;
    end;
    

    Even with the queuing in place to address multithreading concerns, another disadvantage is that things still do not play very well if your server’s OnExecute event or CommandHandlers collection has to send data back to a client in response to a command from the client at the same time that the server is broadcasting messages to those same clients. After a client sends a command and tries to read back a response, it may receive a broadcast instead, and the real response will be received after sending another command later on. The client would have to detect broadcasts so it can keep reading until it gets the response it is expecting.

    You are essentially asking for two separate communication models. One model allows a client to send commands to the server (SignIn/Out, etc), and another model where the server sends real-time broadcasts to clients. Trying to manage those two models over a single connection is doable, but tricky. A simple solution would be to move the broadcasts to another TIdTCPServer that just sends broadcasts and does nothing else. The master can have two TIdTCPServer components running, listening on different ports, and then each slave can have two TIdTCPClient components running, one for sending commands and one for receiving broadcasts. The downside is that each slave would have to keep 2 connections to the master, which can eat up network bandwidth if you have a lot of slaves connected at one time. But it does keep your coding fairly simple on both sides.

    Indy 9:

    procedure TMaster.IdTCPServer1Execute(AThread: TIdPeerThread);
    var
      S: String;
    begin
      S := AThread.Connection.ReadLn;
      if S = 'SignIn' then
        ...
      else if S = 'SignOut' then
        ...
      else
        ...
    end;
    
    procedure TMaster.SendBroadcast(const S: String);
    var
      List: TList;
      I: Integer;
    begin
      List := IdTCPServer2.Threads.LockList;
      try
        for I := 0 to List.Count-1 do
        begin
          try
            TIdPeerThread(List[I]).Connection.WriteLn(S);
          except
          end;
        end;
      finally
        IdTCPServer2.Threads.UnlockList;
      end;
    end;
    

    .

    procedure TSlave.Connect;
    begin
      IdTCPClient1.Connect;
      IdTCPClient2.Connect;
    end;
    
    procedure TSlave.SignIn;
    begin
      IdTCPClient1.SendCmd('SignIn');
      ...
    end;
    
    procedure TSlave.SignOut;
    begin
      IdTCPClient1.SendCmd('SignOut');
      ...
    end;
    
    procedure TSlave.IdTCPClient2Connect(Sener: TObject);
    begin
      Timer1.Enabled := True;
    end;
    
    procedure TSlave.IdTCPClient2Connect(Sener: TObject);
    begin
      Timer1.Enabled := True;
    end;
    
    procedure TSlave.Timer1Elapsed(Sender: TObject);
    var
      S: String;
    begin
      try
        if IdTCPClient2.InputBuffer.Size = 0 then
          IdTCPClient2.ReadFromStack(True, 0, False);
        while IdTCPClient2.InputBuffer.Size > 0 do
        begin
          S := IdTCPClient2.ReadLn;
          ... handle broadcast ...
        end;
      except
        on E: EIdException do
          IdTCPClient2.Disconnect;
      end;
    end;
    

    Indy 10:

    procedure TMaster.IdTCPServer1Execute(AContext: TIdContext);
    var
      S: String;
    begin
      S := AContext.Connection.IOHandler.ReadLn;
      if S = 'SignIn' then
        ...
      else if S = 'SignOut' then
        ...
      else
        ...
    end;
    
    procedure TMaster.SendBroadcast(const S: String);
    var
      List: TList;
      I: Integer;
    begin
      List := IdTCPServer2.Contexts.LockList;
      try
        for I := 0 to List.Count-1 do
        begin
          try
            TIdContext(List[I]).Connection.IOHandler.WriteLn(S);
          except
          end;
        end;
      finally
        IdTCPServer2.Contexts.UnlockList;
      end;
    end;
    

    .

    procedure TSlave.Connect;
    begin
      IdTCPClient1.Connect;
      IdTCPClient2.Connect;
    end;
    
    procedure TSlave.SignIn;
    begin
      IdTCPClient1.SendCmd('SignIn');
      ...
    end;
    
    procedure TSlave.SignOut;
    begin
      IdTCPClient1.SendCmd('SignOut');
      ...
    end;
    
    procedure TSlave.IdTCPClient2Connect(Sener: TObject);
    begin
      Timer1.Enabled := True;
    end;
    
    procedure TSlave.IdTCPClient2Connect(Sener: TObject);
    begin
      Timer1.Enabled := True;
    end;
    
    procedure TSlave.Timer1Elapsed(Sender: TObject);
    var
      S: String;
    begin
      try
        if IdTCPClient2.IOHandler.InputBufferIsEmpty then
          IdTCPClient2.IOHandler.CheckForDataOnSource(0);
        while not IdTCPClient2.IOHandler.InputBufferIsEmpty do
        begin
          S := IdTCPClient2.IOHandler.ReadLn;
          ... handle broadcast ...
        end;
      except
        on E: EIdException do
          IdTCPClient2.Disconnect;
      end;
    end;
    

    If using separate connections for commands and broadcasts is not an option for whatever reason, then you essentially need to design your communication protocol to work asynchronously, meaning a client can send a command to the server and not wait for the response to come back right away. The client would have to do all of its reading from inside a timer/thread, and then identify whether each received message is a broadcast or a response to a previous command and act accordingly:

    Indy 9:

    procedure TSlave.IdTCPClient1Connect(Sender: TObject);
    begin
      Timer1.Enabled := True;
    end;
    
    procedure TSlave.IdTCPClient1Disconnect(Sender: TObject);
    begin
      Timer1.Enabled := False;
    end;
    
    procedure TSlave.PostCmd(const S: String);
    begin
      IdTCPClient1.WriteLn(S);
    end;
    
    procedure TSlave.Timer1Elapsed(Sender: TObject);
    var
      S: String;
    begin
      try
        if IdTCPClient1.InputBuffer.Size = 0 then
          IdTCPClient1.ReadFromStack(True, 0, False);
        while IdTCPClient1.InputBuffer.Size > 0 do
        begin
          S := IdTCPClient1.ReadLn;
          if (S is a broadcast) then
            ... handle broadcast ...
          else
            ... handle a command response ...
        end;
      except
        on E: EIdException do
          IdTCPClient1.Disconnect;
      end;
    end;
    

    Indy 10:

    procedure TSlave.IdTCPClient1Connect(Sender: TObject);
    begin
      Timer1.Enabled := True;
    end;
    
    procedure TSlave.IdTCPClient1Disconnect(Sender: TObject);
    begin
      Timer1.Enabled := False;
    end;
    
    procedure TSlave.PostCmd(const S: String);
    begin
      IdTCPClient1.IOHandler.WriteLn(S);
    end;
    
    procedure TSlave.Timer1Elapsed(Sender: TObject);
    var
      S: String;
    begin
      try
        if IdTCPClient1.IOHandler.InputBufferIsEmpty then
          IdTCPClient1.IOHandler.CheckForDataOnSource(0);
        while not IdTCPClient1.IOHandler.InputBufferIsEmpty do
        begin
          S := IdTCPClient1.IOHandler.ReadLn;
          if (S is a broadcast) then
            ... handle broadcast ...
          else
            ... handle a command response ...
        end;
      except
        on E: EIdException do
          IdTCPClient1.Disconnect;
      end;
    end;
    
    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

Hi I am quite new to php but i have been following some tutorials
I am quite new to ICEfaces but already have experience with JSF/Facelets and the
Quite new to this but i have created a SQL script that groups by
I quite new with CMake, but I have a problem with porting an existing
I am quite new to Lean/Kanban, but have poured over online resources over the
I'm quite new to php and have been reading Larry Ullman book to develop
I'm quite new to c++, but I've got the hang of the fundamentals. I've
Quite new to functional languages, but I'm maintaining someone else's code with a lot
Am quite new to namespacing etc so hopefully this will be straightforward. I have
I am quite new to windows form app development. I have an app installed

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.