【问题标题】:Make socket server accept multiple clients使套接字服务器接受多个客户端
【发布时间】:2011-12-15 20:58:07
【问题描述】:

我想更改我使用的socket class 以接受无限数量的客户。目前它允许一个客户端,一旦该客户端断开连接,服务器就会退出。

#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() 函数,以便它不断等待新客户端连接,一旦完成,为他(客户端)创建一个新线程,或者一个新的处理程序套接字(无论是什么)。

我确实发现this 线程提供了丰富的信息,但我缺乏在上述代码中实际实现它所需的套接字知识。

答案是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 的客户端?

我很抱歉我不能表现出更多的努力,我不喜欢给人懒惰的印象。但是对于我搜索的所有时间以及由此产生的反复试验,我没有太多可以展示的东西。

【问题讨论】:

  • 例如,您可以为每个连接的客户端生成一个新线程。
  • 我会推荐 boost asio。
  • 但是我什么时候会将连接的客户端分叉到新线程?就像..在我设置侦听器端口之前,这意味着每个客户端都需要自己的端口,或者之后。如果之后,我将如何让班级异步接受消息?我真的 100% 不知道该怎么做。
  • '但是在什么时候我会将连接的客户端分叉到新线程' - 在接受()返回服务器客户端套接字之后立即。将套接字从 accept() 传递给新线程。如果您要像这样为每个新的客户端连接创建一个新线程,通常只在客户端-服务器线程中运行同步调用。

标签: c++ windows multithreading sockets


【解决方案1】:

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

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

这是我编写的一个简单代理的一些代码,它对线程使用 boost 并在套接字函数周围使用一个简单的 OOP 包装器。

主线程 - 它创建 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
    }
}

希望有帮助:)

【讨论】:

  • 我开始使用链接的 C 示例 kol,所以我现在没有使用这个,但我相信它以后会有它的用途,谢谢!
【解决方案2】:

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

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

我肯定会使用线程而不是分叉。 Windows 上的 AFAIK 只有 cygwin 能够分叉,但我不会将 cygwin 用于这样的程序。

【讨论】:

  • 您是否有机会提供小的示例代码,以便我知道处理程序套接字是什么样的?还是它已经出现在我的代码中(大部分不是我自己写的)?
  • 您的方法非常低级。用 C#、Java、Delphi 等编写它要容易得多。
  • 一个 Windows 示例(C 语言):mycplus.com/source-code/c-source-code/tcp-client-and-server 作者非常清晰地展示了如何编写客户端和服务器。监听是在一个循环的主线程上完成的,每个传入的请求都由一个单独的线程处理。这是针对 Windows 的,但作者使用的是 Berkeley Sockets API,它也可以在 Linux 和 Mac 上使用。
  • 到目前为止,C 示例似乎运行良好,谢谢。
猜你喜欢
  • 2015-10-01
  • 2023-03-10
  • 2014-08-18
  • 2017-02-25
  • 2013-08-27
  • 2011-08-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多