2

I'd like to change the socket class I am using to accept an infinite amount of clients. At the moment it allows one client, and once that client disconnect the server exits.

#include "stdafx.h"

#include "mySocket.h"
#include "myException.h"
#include "myHostInfo.h"

void main()
{

#ifdef WINDOWS_XP
    // Initialize the winsock library
    WSADATA wsaData;
    try
    {
        if (WSAStartup(0x101, &wsaData))
        {
            myException* initializationException = new myException(0,"Error: calling WSAStartup()");
            throw initializationException;
        }
    }
    catch(myException* excp)
    {
        excp->response();
        delete excp;
        exit(1);
    }
#endif

    // get local server information
    myHostInfo uHostAddress;
    string localHostName = uHostAddress.getHostName();
    string localHostAddr = uHostAddress.getHostIPAddress();
    cout << "------------------------------------------------------" << endl;
    cout << "   My local host information:" << endl;
    cout << "       Name:    " << localHostName << endl;
    cout << "       Address: " << localHostAddr << endl;
    cout << "------------------------------------------------------" << endl;

    // open socket on the local host
    myTcpSocket myServer(PORTNUM);
    cout << myServer;

    myServer.bindSocket();
    cout   << endl << "server finishes binding process... " << endl;

    myServer.listenToClient();
    cout   << "server is listening to the port ... " << endl;

    // wait to accept a client connection.
    // processing is suspended until the client connects
    cout   << "server is waiting for client connecction ... " << endl;

    myTcpSocket* client;    // connection dedicated for client communication
    string clientHost;      // client name etc.
    client = myServer.acceptClient(clientHost);

    cout   << endl << "==> A client from [" << clientHost << "] is connected!" << endl << endl;

    while(1)
    {
        //Send message to the client
        client->sendMessage(std::string("Test"));

        // receive from the client
        string clientMessageIn = "";
        int numBytes = client->recieveMessage(clientMessageIn); //Get message from client, non-blocking using select()
        if ( numBytes == -99 ) break;

        if(clientMessageIn != "")
        {
            std::cout << "received: " << clientMessageIn << std::endl; //What did we receive?

            /* Do somethign with message received here */
        }
    }

#ifdef WINDOWS_XP
    // Close the winsock library

    try
    {
        if (WSACleanup())
        {
            myException* cleanupException = new myException(0,"Error: calling WSACleanup()");
            throw cleanupException;
        }
    }
    catch(myException* excp)
    {
        excp->response();
        delete excp;
        exit(1);
    }

#endif
}

How do I change the main() function so that it is constantly waiting for new clients to connect, and once they do, create a new thread for him (the client), or a new handler socket (whatever that may be).

I did find this thread to be informative, but I lack the required knowledge of sockets to actually implement it in the above code.

The answer states When doing socket communication, you basically have a single listener socket for all incoming connections, and multiple handler sockets for each connected client.

So I am guessing in my code;

myTcpSocket myServer(PORTNUM);
myServer.bindSocket();
myServer.listenToClient();

Would be the listener socket

But where/how would I fork the client who is connecting off to a handler socket ?

I am sorry for not being able to show more effort on my part, I don't like coming across as lazy. But for all the hours I have searched and the trial and error resulting from that, I don't have much to show for it.

Community
  • 1
  • 1
natli
  • 3,782
  • 11
  • 51
  • 82
  • You can for example spawn a new thread for each connected client. – Cyclonecode Dec 15 '11 at 21:00
  • I would recommend boost asio. – Bashwork Dec 15 '11 at 21:03
  • But at what point would I fork the connected client off to the new thread? Like.. before I set the listener port, meaning each client would need its own port, or after. And if afterwards, how would I make the class accept messages asynchronously? I'm really 100% lost on how to do this. – natli Dec 15 '11 at 21:04
  • 'But at what point would I fork the connected client off to the new thread' - immediately after accept() returns with a server<>client socket. Pass the socket from the accept() to the new thread. If you are going to raise a new thread for each new client connection like this, it is usual to just run synchronous calls in the client-server thread. – Martin James Dec 15 '11 at 23:07

2 Answers2

4

The idea is simple, you just wait for incoming connections, and once accepted, pass the socket to a thread.

You need to pass the new socket returned from accept to the new thread; you could either spawn a new thread everytime and pass the socket via argument or add the socket to a shared queue used by a bunch of worker threads.

Here's some code for a simple proxy I wrote, it uses boost for the threads and a simple OOP wrapper around the socket functions.

The main thread - it creates 4 worker threads which idle and wait for the semaphore to be signalled. It pushes all accepted connections to a global queue:

// Global variables

const size_t MAX_THREADS = 4;

queue<Socket> socketBuffer; // Holds new accepted sockets
boost::mutex queueGuard; // Guards the socketBuffer queue
semaphore queueIndicator; // Signals a new connection to the worker threads
bool ctrlc_pressed = false;

// Inside the main function...

boost::thread_group threads;
for(int i = 0; i < MAX_THREADS; i++)
{
    threads.create_thread(boost::bind(&threadHandleRequest, i+1));
}

while(!ctrlc_pressed)
{
    // wait for incoming connections and pass them to the worker threads
    Socket s_connection = s_server.accept();
    if(s_connection.valid())
    {
        boost::unique_lock<boost::mutex> lock(queueGuard);
        socketBuffer.push(s_connection);
        queueIndicator.signal();
    }
}

threads.interrupt_all(); // interrupt the threads (at queueGuard.wait())
threads.join_all(); // wait for all threads to finish

s_server.close();

And the thread code:

bool threadHandleRequest(int tid)
{
    while(true)
    {
        // wait for a semaphore counter > 0 and automatically decrease the counter
        try
        {
            queueIndicator.wait();
        }
        catch (boost::thread_interrupted)
        {
            return false;
        }

        boost::unique_lock<boost::mutex> lock(queueGuard);

        assert(!socketBuffer.empty());

        Socket s_client = socketBuffer.front();
        socketBuffer.pop();

        lock.unlock();

        // Do whatever you need to do with the socket here
    }
}

Hope that helps :)

pezcode
  • 5,490
  • 2
  • 24
  • 37
  • I started using the C example kol linked to so I'm not using this right now, but I'm sure it will have its uses later, thank you! – natli Dec 16 '11 at 13:49
0

When doing socket communication, you basically have a single listener socket for all incoming connections, and multiple handler sockets for each connected client.

That's the point. You need a separate thread for the listener socket. When it receives an incoming request, it starts another thread for a handler socket (which will create and send the response), and starts listening again (you need a loop).

I would definitely use threads instead of forking. AFAIK on Windows only cygwin is able to fork, but I would not use cygwin for such a program.

kol
  • 27,881
  • 12
  • 83
  • 120
  • Is there any chance you could provide small sample code so I know what a handler socket looks like? Or is it already present in my code (didn't write most of it myself)? – natli Dec 15 '11 at 21:06
  • Your approach is very low-level. It's much easier to write this in C#, Java, Delphi etc. – kol Dec 15 '11 at 21:10
  • A Windows example (in C): http://www.mycplus.com/source-code/c-source-code/tcp-client-and-server/ The author shows in a very clear way how to write a client and a server. The listening is done on the main thread in a loop, and each incoming request is handled by a separate thread. This is for Windows, but the author uses the Berkeley Sockets API, which can also be used on Linux and Mac. – kol Dec 15 '11 at 21:23
  • The C example seems to be working pretty well so far, thanks. – natli Dec 16 '11 at 13:48