I am writing a simple client/server chat program with Indy 10. My server (idtcpserver) sends a command to the client, and the client answers, but when more than one client is connected and the server sends a command, all the clients connected send data to the server.
How I can send a command to a specified client and not all?
The only way a command could be sent to all connected clients is if your code is looping through all of the clients sending the command to each one. So simply remove that loop, or at least change it to only send to the specific client that you are interested in.
The best place to send a command to a client, to avoid corrupting the communications with that client due to overlapping commands, is from within that client's own OnExecute
event, eg:
procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
begin
...
if (has a command to send) then
begin
AContext.Connection.IOHandler.WriteLn(command here);
...
end;
...
end;
If you need to send commands to a client from other threads, then it is best to give that client its own queue of outbound commands and then have that client's OnExecute
event send the queue when it is safe to do so. Other threads can push commands into the queue when needed.
type
TMyContext = class(TIdServerContext)
public
ClientName: String;
Queue: TIdThreadSafeStringList;
constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TThreadList = nil); override;
destructor Destroy; override;
end;
constructor TMyContext.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TThreadList = nil);
begin
inherited Create(AConnection, AYarn, AList);
Queue := TIdThreadSafeStringList.Create;
end;
destructor TMyContext.Destroy;
begin
Queue.Free;
inherited Destroy;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
IdTCPServer1.ContextClass := TMyContext;
end;
procedure TForm1.SendCommandToClient(const ClientName, Command: String);
var
List: TList;
I: Ineger;
Ctx: TMyContext;
begin
List := IdTCPServer1.Contexts.LockList;
try
for I := 0 to List.Count-1 do
begin
Ctx := TMyContext(List[I]);
if Ctx.ClientName = ClientName then
begin
Ctx.Queue.Add(Command);
Break;
end;
end;
finally
IdTCPServer1.Context.UnlockList;
end;
end;
procedure TForm1.IdTCPServer1Connect(AContext: TIdContext);
var
List: TList;
I: Ineger;
Ctx, Ctx2: TMyContext;
ClientName: String;
begin
Ctx := TMyContext(AContext);
ClientName := AContext.Connection.IOHandler.ReadLn;
List := IdTCPServer1.Contexts.LockList;
try
for I := 0 to List.Count-1 do
begin
Ctx2 := TMyContext(List[I]);
if (Ctx2 <> Ctx) and (Ctx.ClientName = ClientName) then
begin
AContext.Connection.IOHandler.WriteLn('That Name is already logged in');
AContext.Connection.Disconnect;
Exit;
end;
end;
Ctx.ClientName = ClientName;
finally
IdTCPServer1.Context.UnlockList;
end;
AContext.Connection.IOHandler.WriteLn('Welcome ' + ClientName);
end;
procedure TForm1.IdTCPServer1Disconnect(AContext: TIdContext);
var
Ctx: TMyContext;
begin
Ctx := TMyContext(AContext);
Ctx.ClientName = '';
Ctx.Queue.Clear;
end;
procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
Ctx: TMyContext;
Queue: TStringList;
begin
Ctx := TMyContext(AContext);
...
Queue := Ctx.Queue.Lock;
try
while Queue.Count > 0 do
begin
AContext.Connection.IOHandler.WriteLn(Queue[0]);
Queue.Delete(0);
...
end;
...
finally
Ctx.Queue.Unlock;
end;
end;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With