views:

64

answers:

3

Hi I am working on an assignment writing multi threaded client server. So far I have done is open a socket in a port and forked two thread for listening and writing to client. But I need to connect two type of clients to the server and service them differently. My question is what would be my best approach?

I am handling connection in a class which has a infinite loop to accept connection. When ever a connection is accepted, the class create two thread to read and write to client? Now if I wnat to handle another client of different type, what should we do?

Do I need to open another port? or is it possible to service through same port? May be if it is possible to identify the type of client in the socket than I can handle messages differently.

Or do you suggest like this?

  1. Fork two thread for two type of client and monitor inbound connection in each thread in different port.
  2. when a connection accepted each thread spawn another two thread for listening and writing.

please make a suggestion.

A: 

Different protocols are generally serviced on different ports. However, you could service both types of clients over the same port by negotiating the protocol to be used. This can be as simple as the client sending either HELO or EHLO to request one or another kind of service.

Jeremy W. Sherman
+2  A: 

Perhaps you'll get a better answer from a Unix user, but I'll provide what I know.

Your server needs a thread that opens a 'listening' socket that waits for incoming connections. This thread can be the main thread for simplicity, but can be an alternate thread if you are concerned about UI interaction, for example (in Windows, this would be a concern, not sure about Unix). It sounds like you are at least this far.

When the 'listening' socket accepts a connection, you get a 'connected' socket that is connected to the 'client' socket. You would pass this 'connected' socket to a new thread that manages the reading from and writing to the 'connected' socket. Thus, one change I would suggest is managing the 'connected' socket in a single thread, not two separate threads (one for reading, one for writing) as you have done. Reading and writing against the same socket can be accomplished using the select() system call, as shown here.

When a new client connects, your 'listening' socket will provide a new 'connected' socket, which you will hand off to another thread. At this point, you have two threads - one that is managing the first connection and one that is managing the second connection. As far as the sockets are concerned, there is no distinction between the clients. You simply have two open connections, one to each of your two clients.

At this point, the question becomes what does it mean to "service them differently". If the clients are expected to interact with the server in unique ways, then this has to be determined somehow. The interactions could be determined based on the 'client' socket's IP address, which you can query, but this seems arbitrary and is subject to network changes. It could also be based on the initial block of data received from the 'client' socket which indicates the type of interaction required. In this case, the thread that is managing the 'connected' socket could read the socket for the expected type of interaction and then hand the socket off to a class object that manages that interaction type.

I hope this helps.

Matt Davis
A: 

You can handle the read-write on a single client connection in one thread. The simplest solution based on multiple-threads will be this:

// C++ like pseudo-code
while (server_running)
{
    client = server.accept();
    ClientHandlingThread* cth = CreateNewClientHandlingThread(client);
    cth->start();
}

class ClientHandlingThread
{
    void start()
    {
         std::string header = client->read_protocol_header();
         // We get a specific implementation of the ProtocolHandler abstract class
         // from a factory, which create objects by inspecting some protocol header info. 
         ProtocolHandler* handler = ProtocolHandlerFactory.create(header);         
         if (handler)
             handler->read_write(client);
         else
            log("unknown protocol")
    }    
};

To scale better, you can use a thread pool, instead of spawning a new thread for each client. There are many free thread pool implementations for C++.

while (server_running)
{
    client = server.accept();
    thread_pool->submit(client);
    cth->start();
}

The server could be improved further by using some framework that implements the reactor pattern. They use select or poll functions under the hood. You can use these functions directly. But for a production system it is better to use an existing reactor framework. ACE is one of the most widely known C++ toolkits for developing highly scalable concurrent applications.

Vijay Mathew