Indy TCP Client/Server with the client acting as a server

40,518

Solution 1

There is nothing preventing you from doing this with Indy's TIdTCPServer component.

A TIdTCPServer only sets up the connection. You'll need to implement the rest. So the sequence of the actual sending and receiving can be whatever you want.

Put this code in your TIdTCPServer component's OnExecute event:

var
  sName: String;
begin
  // Send command to client immediately after connection
  AContext.Connection.Socket.WriteLn('What is your name?');
  // Receive response from client
  sName := AContext.Connection.Socket.ReadLn;
  // Send a response to the client
  AContext.Connection.Socket.WriteLn('Hello, ' + sName + '.');
  AContext.Connection.Socket.WriteLn('Would you like to play a game?');
  // We're done with our session
  AContext.Connection.Disconnect;
end;

Here's how you can setup the TIdTCPServer really simply:

IdTCPServer1.Bindings.Clear;
IdTCPServer1.Bindings.Add.SetBinding('127.0.0.1', 8080);
IdTCPServer1.Active := True;

This tells the server to listen on the loopback address only, at port 8080. This prevents anyone outside of your computer from connecting to it.

Then, to connect your client, you can go to a Windows command prompt and type the following:

telnet 127.0.0.1 8080

Here's the output:

What is your name?

Marcus

Hello, Marcus.

Would you like to play a game?

Connection to host lost.

Don't have telnet? Here's how to install telnet client on Vista and 7.

Or with a TIdTCP Client, you can do this:

var
  sPrompt: String;
  sResponse: String;
begin
  // Set port to connect to
  IdTCPClient1.Port := 8080;
  // Set host to connect to
  IdTCPClient1.Host := '127.0.0.1';
  // Now actually connect
  IdTCPClient1.Connect;
  // Read the prompt text from the server
  sPrompt := IdTCPClient1.Socket.ReadLn;
  // Show it to the user and ask the user to respond
  sResponse := InputBox('Prompt', sPrompt, '');
  // Send user's response back to server
  IdTCPClient1.Socket.WriteLn(sResponse);
  // Show the user the server's final message
  ShowMessage(IdTCPClient1.Socket.AllData);
end;

An important thing to note here is that the ReadLn statements wait until there is data. That's the magic behind it all.

Solution 2

If your commands are textual in nature, then have a look at the TIdCmdTCPClient component, it is specifically designed for situations when the server is sending commands instead of the client. The server can use TIdContext.Connection.IOHandler.WriteLn() or TIdContext.Connection.IOHandler.SendCmd() to send the commands.

Solution 3

When the client connects to the server, the server has an OnConnect event with an AContext: TIdContext parameter.

A property of this is AContext.Connection, which you can store outside of that event (say, in an Array). If you pair it with the IP or better yet a generated Session ID, then reference that Connection by that criteria, you can then have the server send adhoc commands or messages to the client.

Hope this helps!

Solution 4

normally the client and the server side have a thread that is reading incoming telegrams, and sending pending telegrams...but this kind of protocols (send/receive, when and what) depend of the application.

Solution 5

A very good starting point how the client side can be implemented using a thread, listening for messages from the server, is the Indy Telnet client component (TIdTelnet in the Protocols folder).

The Indy telnet client connects to the telnet server and uses only one socket to write and read data. Reading happens in a listener thread.

This design can easily be adapted to build distributed messaging software like chat etc., and also shows how easy the protocol can be decoupled from the network layer using blocking sockets.

Share:
40,518
Conrad Hildebrand
Author by

Conrad Hildebrand

Updated on July 09, 2022

Comments

  • Conrad Hildebrand
    Conrad Hildebrand almost 2 years

    How can Indy's TIdTCPClient and TIdTCPServer be used in the following scenario:

    Client  ---------- initate connection -----------> Server
    ...
    Client  <---------------command------------------- Server
    Client  ----------------response-----------------> Server
    ...
    Client  <---------------command------------------- Server
    Client  ----------------response-----------------> Server
    

    The client initiates the connection, but acts as a "server" (waiting for commands and executing them).

    The OnExecute approach of TIdTCPServer does not work well in this case (at least I am not getting it to work well). How could I do this?

    I hope the question is clear enough.

  • LaKraven
    LaKraven over 12 years
    Yes, and this solution allows for that! The server just needs to store each client's connection so that it can pass messages or commands directly to one or more clients without being limited to responding to client commands first!
  • LightBulb
    LightBulb over 12 years
    And how will the Client know that it received a command if there is no listening thread on client side?
  • Daniel Rikowski
    Daniel Rikowski over 12 years
    Is this really a design flaw of Indy? I always thought this was a TCP limitation...
  • LaKraven
    LaKraven over 12 years
    The client has a persisted connection to the server, so of course it has a listening thread on the client side ;) Should point out I've actually made client/server systems with bi-directional communication before (repeatedly).
  • mjn
    mjn over 12 years
    The 'magic' should be put in its own thread so that the application can continue while the server has nothing to say
  • Marjan Venema
    Marjan Venema over 12 years
    Oh, well in that case I guess we are like Tom Cruise in "Mission impossible" because without any pulling from our clients, our server is sending messages to our clients, for example when it is shutting down, or to tell all connected clients of changes in certain data...
  • mjn
    mjn over 12 years
    IIUC TCP/IP communication is: 1. Client connects to Server (the party which initiates the connection is the client by definition), 2. Both parties can send data at any time (because a TCP/IP socket is bidirectional)
  • Mike Taylor
    Mike Taylor over 12 years
    I have implemented a protocol like this before and successfully based it on the telnet client code.
  • Jerry Dodge
    Jerry Dodge over 9 years
    This is not a limitation at all. The server is more than free to send data to the client whenever it wants, and the client is more than welcome to check for incoming data. Indy is extremely capable. It may take a while to learn all about it, but it does all that and then some.