Jump to content
limelect

IdTCPServer IdTCPClient knowledge.

Recommended Posts

I need just advice.  I have no problems.

I have IdTCPServer IdTCPClient with more than one client.

I am using the server as a response to clients.

However, I would like to send specific client information from the server, which is impossible.

However, I saw a TIMER answer which I liked at the end of

https://stackoverflow.com/questions/5530038/how-to-handle-received-data-in-the-tcpclient-delphi-indy

 

Here comes the knowledge. If the server sends a message to whom does he send it? To all clients? Or else?

The server message is not a response to the client message.

I could not find a write instruction with the peer number.

As I have the PeerPort   on my server software (I keep a list) 

I suspect it is not possible to send from server text to a specific Client.

A solution is to embed in the message a sign that a specific client will recognize.

Any other solution in your opinion?

 

 

Edited by limelect

Share this post


Link to post

I have a nice solution You can send any string to any client

This is just the general idea and someone mite use this

On the client put the timer

procedure TForm1.Timer1Timer(Sender: TObject);
var
s: string;
begin
if not IdTCPClient1.Connected then Exit;
if IdTCPClient1.IOHandler.InputBufferIsEmpty then Exit;
s := IdTCPClient1.IOHandler.InputBufferAsString;
Memo1.Lines.Add('Received: ' + s);

end;
 

On the server Use the list

 

procedure TForm2.Button2Click(Sender: TObject);
var
List: TList;
T:TStringList;
i:Integer;
begin
 T:=TStringList.Create;
 t.Add('1234');
  List := IdTCPServer1.Contexts.LockList;
  TIdContext(List.Items[0]).Connection.WriteRFCStrings(t);
  IdTCPServer1.Contexts.UnlockList;
  t.Free;
end;
 

Keep the client list List.Items[0]<< 1 2 3 4 and so on

It needs more work but it works This is a general idea;

I added listview with the client list and used it

And it works nicely.

 

TIdContext(List.Items[StrToInt( ListView1.Selected.Caption)]).Connection.WriteRFCStrings(t);
 

 

 

Share this post


Link to post
21 hours ago, limelect said:

I suspect it is not possible to send from server text to a specific Client.

A solution is to embed in the message a sign that a specific client will recognize.

Any other solution in your opinion?

 

With Indy, the recommended way to send a message to a specific client is to assign an unique client id to new connections in the OnConnect method of the server. The server application then can use the client id to limit the message to one (or some) of the connected clients. Technically, the client id would be a property in a subclass of the TIdContext class, and this class must be "registered" with the Indy TCP server. There are plenty of articles on the web covering this technique.

Edited by mjustin

Share this post


Link to post
7 hours ago, mjustin said:

With Indy, the recommended way to send a message to a specific client is to assign an unique client id

I think i do not understand.

The peerport is assigned AFTER connection. An ID? where is it. Do you know it

before connection.Does it equel peerport so it is the same.

In my techinc LIST has all necesery information to identify the client.

PeerPort changes every time you reconnect to server and is not fixed.

Further more in the SERVER I associate user to peerport of the client so I know 

who is using the client.

In the IdTCPServer1Execute I know who is connected as I send info from client to server

So what is left to do is pick the right info from my LIST port=user and sent what ever to port.

Share this post


Link to post

To clarify the application

The client is a worker. He has a number in the server DB.

Connection is done when the worker send his ID=DB number

and get needed information from server.

The exchange is very simple.

Obviusly the server gets worker number and associate the peerport to it.

I get all kind of messeges for worker where he get data from DB in the server.

Share this post


Link to post

When server accepts client connection, it creates its internal client object.

You can send anything to that server-side client object. But client must be ready to read that data which it hadn't requested.

You can assign any data to a server-side client object when it gets created to identify it.

Share this post


Link to post

What @mjustin meant is that normally a custom CONTEXT is used where for each connection received additional data can be inserted, such as an ID, a particular string of marking, or any other information we need for the activity.

Example:

 

  TMyContext = class(TIdServerContext) // <-- must derive from TIdServerContext
    public
      /// <summary> This is a new RefID field </summary>
      RefID: UInt64;
	  ChargeStr: string;
   end;

   //Somewhere, BEFORE activate the server assign MyContext to the component

   begin
     IdTCPServer1.ContextClass := TMyContext;
	 IdTCPServer1.Activate := True;
   end; 

   //In the Connect event you can initialize the Context
   procedure IdTCPServer1Connect(AContext: TIdContext);
   var Ctx: TMyContext;
   begin
  	 Ctx := TMyContext(AContext);
	 Ctx.RefID := 0;
	 Ctx.ChargeStr := '';
   end;

   //Now you can use the new context whereever you want

 

  • Thanks 1

Share this post


Link to post
On 8/26/2023 at 9:01 AM, limelect said:

I am using the server as a response to clients.

However, I would like to send specific client information from the server, which is impossible.

It is not impossible.  You simply send the desired data using the IOHandler of the TIdContext object that represents the client on the server.  But, you do have to be VERY careful when sending unsolicited (ie, non-requested) data from the server to a client.  If you are not careful and send the unsolicited data incorrectly, you can easily corrupt your TCP communication.  You need to make sure the client is not trying to request data at the same time that you are sending unsolicited data, or the client might misinterpret the data.  Most protocols tat support unsolicited data design their messages in such a way that clients can identify what messages are unsolicited and what messages are responses to requests.

 

Is this particular client EVER requesting data from the server?  Or does the server ALWAYS initiate the sending?  It makes a BIG difference in how you design your protocol and manage it in your code.  So it is kind of difficult to answer your question definitively without knowing more about your communication needs.

On 8/26/2023 at 9:01 AM, limelect said:

Here comes the knowledge. If the server sends a message to whom does he send it? To all clients? Or else?

TCP is a 1:1 connection between client and server.  There is no broadcasting.  The data will be sent only to the specific client that you send it to.

On 8/26/2023 at 9:01 AM, limelect said:

The server message is not a response to the client message.

Are there ANY requests being sent from this client to the server?

 

If yes, then you need to synchronize your sends so that unsolicited data does not overlap response data.  This is typically done by creating a per-client queue of outgoing data, and then having the server's OnExecute event send the calling client's queue when it is safe to do so, ie when it is not servicing any requests.

 

If no, then you can simply send data directly to the client whenever you want.

On 8/26/2023 at 9:01 AM, limelect said:

I could not find a write instruction with the peer number.

There is no "peer number" in TCP.  But, the server does have a unique socket handle (ie, TIdContext object) for each connected client.  And you can always assign your own ID to each client, as well.  You would simply send to whichever socket/context that you are interested in.  This is typically handled inside the server's OnExecute event, using the TIdContext that is passed in as a parameter.  But, depending on your needs, you can also search the server's Contexts list for the desired client and then send to it (if/when safe to do so).

On 8/26/2023 at 9:01 AM, limelect said:

As I have the PeerPort   on my server software (I keep a list)

The PeerPort by itself is not adequate enough to uniquely identify clients.  If clients connects to the server from different networks, they could use the same PeerPort.  You would need the combination of PeerIP+PeerPort to uniquely identify each client.  Better to just use the TIdContext objects instead, since they are already unique.

On 8/26/2023 at 9:01 AM, limelect said:

I suspect it is not possible to send from server text to a specific Client.

Yes, it is.

  • Thanks 1

Share this post


Link to post

Thank you all. I was thinking of using the above when someone changes data in the server

and notifying the client.

@Remy Lebeau Thanks very enlightening. For now, the clients are those that initiate

the need for data from the server.

Share this post


Link to post
13 hours ago, Remy Lebeau said:

Better to just use the TIdContext objects instead, since they are already unique.

Just for knowledge. How will you save TIdContext and then interrogate the list?

In my case, I use a stringlist with peernumber=worker# it is very

helpful to search for peernumber. or vice versa.

clients.IndexOfName(IntToStr(PeerPort)); in one case. or vice-versa.

P.S. I do not suspect using outside the network of the project.

Edited by limelect

Share this post


Link to post
10 hours ago, limelect said:

Just for knowledge. How will you save TIdContext and then interrogate the list?

In my case, I use a stringlist with peernumber=worker# it is very

helpful to search for peernumber. or vice versa.

clients.IndexOfName(IntToStr(PeerPort)); in one case. or vice-versa.

P.S. I do not suspect using outside the network of the project.

Again, the PeerPort alone is typically not unique enough to identify clients.  You are better off assigning your own custom IDs to the TIdContext objects when clients connect/login, eg:

type
  TMyContext = class(TIdServerContext)
    UserID: string;
  end;

var
  Clients: TIdThreadSafeStringList;

procedure TMyForm.FormCreate(Sender: TObject);
begin
  Clients := TIdThreadSafeStringList.Create;
  IdTCPServer1.ContextClass := TMyContext;
  IdTCPServer1.Active := True;
end;

procedure TMyForm.FormDestroy(Sender: TObject);
begin
  IdTCPServer1.Active := False;
  Clients.Free;
end;

procedure TMyForm.IdTCPServer1Connect(AContext: TIdContext);
var
  UserID: string;
  List: TStringList;
begin
  UserID := ...; // read user ID from client
  List := Clients.Lock;
  try
    if List.IndexOf(UserID) <> -1 then
    begin
      AContext.Connection.Disconnect;
      Exit;
    end;
    List.AddObject(UserID, AContext);
    TMyContext(AContext).UserID := UserID;
  finally
    Clients.Unlock;
  end;
end;

procedure TMyForm.IdTCPServer1Disconnect(AContext: TIdContext);
begin
  if TMyContext(AContext).UserID <> '' then
    Clients.Remove(TMyContext(AContext).UserID);
end;

And then you can search your Client's list for your user IDs whenever you need, and then you'll have access to the TIdContext object.  Just be careful accessing it if it happens at the same time that the client disconnects.  That's why if you intend to send data from outside of the server's own threads, it is best to add a queue to the TIdContext and have the OnExecute event send the queue when safe to do so.

  • Thanks 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×