Make socket server accept multiple clients
Solution 1
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.
Solution 2
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 :)
natli
Updated on June 05, 2022Comments
-
natli almost 2 years
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.
-
natli over 12 yearsIs 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)?
-
kol over 12 yearsYour approach is very low-level. It's much easier to write this in C#, Java, Delphi etc.
-
kol over 12 yearsA Windows example (in C): 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.
-
natli over 12 yearsThe C example seems to be working pretty well so far, thanks.
-
natli over 12 yearsI 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!