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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8526478/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 18:40:21  来源:igfitidea点击:

Make socket server accept multiple clients

c++windowsmultithreadingsockets

提问by natli

I'd like to change the socket classI 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).

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

I did find thisthread 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.

答案指出 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

将是 listener socket

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

但是我将在哪里/如何分叉连接到 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.

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

采纳答案by kol

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.

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

回答by pezcode

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 acceptto 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.

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

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.

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

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:

主线程 - 它创建 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();

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 :)

希望有帮助:)