使套接字服务器接受多个客户端

Make socket server accept multiple clients

本文关键字:客户端 套接字 服务器      更新时间:2023-10-16

我想改变我正在使用的套接字类来接受无限数量的客户端。目前它只允许一个客户端连接,一旦客户端断开连接,服务器就退出。

#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
}

我如何改变main()函数,使它不断等待新的客户端连接,一旦他们做了,为他创建一个新的线程(客户端),或一个新的处理程序套接字(无论可能是什么)。

我确实发现这个线程很有用,但是我缺乏在上面的代码中实际实现它所需的套接字知识。

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

所以我猜在我的代码;

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

就是listener socket

但是我在哪里/如何分叉连接到handler socket的客户端?

我很抱歉我没能表现出更多的努力,我不喜欢给人留下懒惰的印象。但是,尽管我花了那么多时间搜索,反复试验,却没有什么成果。

这个想法很简单,您只需等待传入的连接,一旦接受,将套接字传递给线程。

您需要将accept返回的新套接字传递给新线程;您可以每次生成一个新线程并通过参数传递套接字,或者将套接字添加到一堆工作线程使用的共享队列中。

这是我写的一个简单代理的一些代码,它使用boost的线程和一个简单的面向对象封装套接字函数。

主线程-它创建了4个空闲的工作线程并等待要发出信号的信号量。它将所有接受的连接推送到全局队列:

// 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();

和线程代码:

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
    }
}

希望有帮助

在进行套接字通信时,基本上只有一个侦听器的多个处理程序套接字每个连接的客户端。

这才是重点。侦听器套接字需要一个单独的线程。当它接收到一个传入请求时,它为一个处理程序套接字启动另一个线程(它将创建并发送响应),并再次开始侦听(您需要一个循环)。

我肯定会使用线程而不是分叉。据我所知,在Windows上只有cygwin能够分叉,但我不会在这样的程序中使用cygwin。