views:

852

answers:

7

I wanna build a TCP/IP server that will be used by up to 100 concurrent clients, but still not sure how to get started.

at least I need the server to this:

  1. Listening to client, and store all them on array or list.
  2. for each client, it need to receive and send data based on it's client status.
  3. The server should update the clients list when someone connect or disconnect.
  4. Prefer to work as service with GUI to manage it.

Could anyone help how to get start with that,I looked at indy sample but they didn't help, also looked for most components but still searching.

A: 

I've recently run into this problem. Here is a link to a C++ example using EPOLL to manage hundreds of multicast sockets. Yours would be TCP/IP but thats really just a simple change.

Which ever method you choose, for 100+ simultaneous sockets/clients you will want to use a thread model that relies on poll, select, or preferable epoll if your platform supports it.

http://anthonyvirtuoso.com/public/dokuwiki/doku.php/projects:multiplexreceiverepoll

@jfawcett - select on more than ~50 FDs comes with a fairly significant CPU hit depending on how often you actualyl perform the select. In my comment above that sample class was originally using select but after seeing the CPU cost (valgrind w/callgrind) i switched to epoll. But certainly select is a valid option.

avirtuos
+3  A: 

On a Windows platform you are probably best to avoid select for large numbers of concurrent connections (100 isn't a large number of connections though). However, avirtuos is correct in that you want to avoid any 'thread per connection' models. The most efficient approach on Windows is to use overlapped I/O and I/O Completion Ports. This allows you to manage 10s of thousands of connections with a small number of threads (2 or 3, perhaps).

I don't have any Delphi experience so I've no idea how easy it is to interact with C++ code BUT I have a free C++ I/O Completion Port server framework (available from here) that would, at least, show you how the standard Win32 I/O Completion Port API operates. It might be useful to you and you might be able to do something similar in Delphi.

Len Holgate
+1  A: 

100 sockets isn't that many. You could go to all the trouble of using something like epoll(), but for this case I'd just set up a single FD_SET with all the sockets, select() on the entire set, then check each one, and process them in sequence. If I had to do something that was potentially time-consuming, I'd use a thread pool for the message handlers.

jfawcett
A: 

Some excellent component sets that are useful for situations like yours (and much more) are kbmMW and RemObjects. There are some other good sets, too, I think, you could do a search of archive or ask a question in Embarcaderos' Delphi thirdpartytools newsgroup. You can search archives here: http://codenewsfast.com/

Herbert Sitz
+5  A: 

You need to use the TidTCPServer which is multithreaded inside. No need for you to manage the threads. Everything is transparent, so in the way in which you write the application for one client, in (almost) the same way you will write it for many. See the OnConnect event. There is a TidContext parameter which has a TThreadList inside. You can use that event to 'register'/add your clients to your custom array/list and OnDisconnect to remove the clients.

The OnExecute event is triggered when the server receives a message. Use its parameters to read the message which is sent.

Also, you need another application which will be your client, using TidTCPClient. In this application you will set the address of your server (see the Host property) as well as the Port which should match with the server's one. You should call Connect (when the server is running) and to send strings you have SendCmd method. (Also see IOHandler.WriteLn if you want)

There are also other things but I think that's enough to get you started. Also you can post in Embarcadero's forums in the .Delphi.Winsock forum where the Indy team members are floating over. Or perhaps you can ask directly in .Delphi.Non-Technical and the guys there will guide you.

Another approach is DataSnap which is a more object-oriented layer over Indy (not to be confused with DBX) which gives your JSON, REST and other goodies. See for a small review here.

How can I get the clientID?, because I have external even and I should respond to the specific client.
DelphiDev
The `Execute` event has as parameter the `AContext: TidContext`. So you have `AContext.Connection` where you have enough methods to send data to the client eg. `AContext.Connection.SendCmd`. Also, you have the `AContext.Connection.Socket` property (IIRC) which has one of its (sub)properties the IP address of the client which sent the message. You can use this as a 'clientID' if you want to do logging etc.
+3  A: 

And another Delphi library option would be synapse which provides a simple framework that can easily be extended. There is an IOCPPool demo available in the contributed files which may be of assistance.

Synapse is more of a framework of classes than a component library. There is a vibrant and active user community that is ready to support any challenges. I use this library in Delphi 2010 without any problems (although I use the latest development version from SVN).

Being as they are not components, it is very easy to use the classes in simple console applications or windows services.

skamradt
thank u, i looked at synapse, but i found it harder than indy, and not much docs or samples to get me started.
DelphiDev
There are quite a few demos in the default install that should help, as well as the contributed files. When all else fails, ask your specific question here or on the synapse listserv.
skamradt
+1  A: 

Indy is your best choice : 1000 clients are not that much : I did develop a server that had to serve 4-5 k clients and it's working as a charm.

  1. Listening to client, and store all them on array or list. + 3. The server should update the clients list when someone connect or disconnect.

--> As for the clients list, you could loop through the TThreadList member of TidTCPServer (version 9.0) that stores all the "alive" threads, each thread is "equivalent" to a client connection, although threads could outlive a client's connection, but you could fix this by setting an appropriate connection's timeout value. If you want you could also maintain your own Clients' List (inherit from TList for instance or create a Generics.Collection): you would add the client info's after the onConnect event (tidPeerThread class exposes all the client infos: IP...)

You would then loop through this list periodically and check for alive connections (ping command like) and kill/delete all zombies.

[indy documentation] Event handler for peer thread connection attempts.

property OnConnect: TIdServerThreadEvent;

Description

OnConnect is an event handler for TIdServerThreadEvents. OnConnect occurs when a TIdPeerThread attempts to connect to a TIdTCPServer.

OnConnect receives AThread as a parameter, representing the TIdPeerThread thread that is requesting the connection.

Assign a TIdServerThreadEvent event handler procedure to OnConnect. [/indy documentation]

  1. for each client, it need to receive and send data based on it's client status : --> check the chat client and server demo source code for a detailed example.

  2. Prefer to work as service with GUI to manage it : you could develop a service application that would log all of its activity in a DB and a second app that would access that db and show all available stats (clients number...).

and here are the links to Indy 9.0 (sources and documentation) : http://www.indyproject.org/downloads/Indy_9_00_14_src.zip http://www.indyproject.org/downloads/Indy-9-0-Help-WinHelp.zip

And here is an Indy book although I don't think you would need it after reading the documentation: Indy in depth : http://www.atozed.com/Indy/Book/index.EN.aspx

Look here for a good tutorial : http://www.devarticles.com/c/a/Delphi-Kylix/Creating-Chat-Application-with-Borland-DelphiIndy-The-Client/

Good Luck