Заставить сокет-сервер принимать несколько клиентов - PullRequest
2 голосов
/ 16 декабря 2011

Я бы хотел изменить класс сокета , который я использую для приема бесконечного количества клиентов.На данный момент он позволяет одному клиенту и после этого клиента отключить сервер.

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

Извините, что не смог проявить больше усилий с моей стороны,Я не люблю показаться ленивым.Но за все часы, которые я искал, и из-за этого были проб и ошибок, мне особо нечего показать.

Ответы [ 2 ]

3 голосов
/ 16 декабря 2011

Идея проста: вы просто ждете входящих подключений и, как только принимаете, передаете сокет потоку.

Вам необходимо передать новый сокет, возвращенный из accept, в новый поток; Вы можете либо порождать новый поток каждый раз и передавать сокет через аргумент, либо добавить сокет в общую очередь, используемую группой рабочих потоков.

Вот некоторый код для простого прокси, который я написал, он использует 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
    }
}

Надеюсь, это поможет:)

0 голосов
/ 16 декабря 2011

При работе с сокетом у вас в основном один слушатель сокет для всех входящих соединений, и несколько сокетов обработчика для каждый подключенный клиент.

В этом все дело. Вам нужен отдельный поток для сокета слушателя. Когда он получает входящий запрос, он запускает другой поток для сокета-обработчика (который создаст и отправит ответ) и снова начинает слушать (вам нужен цикл).

Я бы определенно использовал темы вместо разветвления. AFAIK на Windows, только Cygwin способен форк, но я бы не стал использовать Cygwin для такой программы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...