Reputation: 1611
I started playing with Indy 10 (from Delphi XE3) and TCP connections recently. I already could, with your help (special thanks to Remy Lebeau), build a simple server application for manage client connections (see here Delphi - Simple TCP client / server using Indy to check clients status). I am using a listbox to add connected clients. See the code:
procedure TfrmMain.TCPServerConnect(AContext: TIdContext);
var
Host: String;
begin
Host := UpperCase(GStack.HostByAddress(AContext.Binding.PeerIP));
TThread.Queue(nil,
Procedure
begin
ListBox.Itens.Add(Host);
Log('Connected - ' + Host);
With TCPServer.Contexts.LockList Do
Try
StatusBar.Panels[0].Text := 'Connected Clients: ' + IntToStr(Count);
Finally
TCPServer.Contexts.UnlockList;
end;
end
);
end;
Now I am trying to send a "hello" from server to a specific client in the list. My idea was to click to select a client hostname in the listbox, then click a button to send the message. But what I am researching is that things are not so easy as I am thinking...
Please, can some Indy expert point me the correct direction (with Indy 10)?
Thanks!
Upvotes: 2
Views: 5467
Reputation: 595305
A hello would most commonly be sent from the OnConnect
event, where you have direct access to the TIdContext
for the client that connected.
That being said, if you want to send data to a specific client from outside of the server events, then you need to keep track of the TIdContext
object for the desired client, or look for it in the server's Contexts
list. In this particular example, you can store the TIdContext
object pointer in the ListBox itself, then retrieve it when needed, eg:
procedure TfrmMain.TCPServerConnect(AContext: TIdContext);
var
Host: String;
begin
Host := UpperCase(GStack.HostByAddress(AContext.Binding.PeerIP));
TThread.Queue(nil,
procedure
begin
ListBox.Items.AddObject(Host, AContext);
Log('Connected - ' + Host);
With TCPServer.Contexts.LockList Do
Try
StatusBar.Panels[0].Text := 'Connected Clients: ' + IntToStr(Count);
Finally
TCPServer.Contexts.UnlockList;
end;
end
);
end;
procedure TfrmMain.TCPServerDisconnect(AContext: TIdContext);
var
Host: String;
begin
Host := UpperCase(GStack.HostByAddress(AContext.Binding.PeerIP));
TThread.Queue(nil,
procedure
var
Index: Integer;
begin
Index := ListBox.Items.IndexOfObject(AContext);
if Index <> -1 then
ListBox.Items.Delete(Index);
Log('Disconnected - ' + Host);
With TCPServer.Contexts.LockList Do
Try
StatusBar.Panels[0].Text := 'Connected Clients: ' + IntToStr(Count);
Finally
TCPServer.Contexts.UnlockList;
end;
end
);
end;
procedure TfrmMain.sendButtonClick(Sender: TObject);
var
Index: Integer;
Ctx: TIdContext;
begin
Index := ListBox.ItemIndex;
if Index = -1 then Exit;
Context := TIdContext(ListBox.Items.Objects[Index]);
// use Context as needed...
end;
Granted, this is not the safest approach, but it will get you started. Things you need to consider:
A client might disconnect and free its TIdContext
object before you have removed it from the ListBox. You should make sure the object is still in the server's Contexts
list before using it.
Sending unsolicited data from a button event (or any other non-server event) is not thread-safe. Your communications can become corrupted if you send data to the same client from other threads, in particular from the server's events, without syncing the threads. You should be managing the communications from within the server events only. If you need to send data from outside the events, it is safer to put the data into a per-client thread-safe queue, and then have the OnExecute
event send the content of the queue when it is safe to do so. I have posted examples of that many times before in several different forums, so it should not be hard to find them online.
The OnDisconnect
event is not a good place to look up client details, like its hostname. You should do that in the OnConnect
and/or OnExecute
event and then cache the value for later use. You can use the TIdContext.Data
property for that purpose, or derive a new class from TIdServerContext
and set the server's ContextClass
property before activating the server.
Upvotes: 4