Как подключить несколько клиентов к одному серверу в c++ на Windows-Visual Studio? [дубликат]



На этот вопрос уже есть ответ здесь:



Я написал серверную программу на C++, а также клиентскую программу на C++.
Оба работают нормально, но если один клиент взаимодействует с сервером, то другой клиент не может подключиться к тому же серверу. Если предположим когда я закрываю клиент 1, то и мой 2-й клиент не может подключиться к серверу. Я запустил свой сервер с несколькими потоками для подключения к нескольким клиентам, но подключается только один клиент.



Моя серверная программа:



#include <iostream>
#include <winsock2.h>
#include <Windows.h>
#include <process.h>
#include <thread>
#pragma comment(lib,"ws2_32.lib")

static const int num_of_threads = 2;

void client_disconnected(SOCKET Socket);

void start_server()
{
WSADATA WsaDat;
if(WSAStartup(MAKEWORD(2,2),&WsaDat)!=0)
{
std::cout<<"WSA Initialization failed!rn";
WSACleanup();
system("PAUSE");
}

SOCKET Socket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(Socket==INVALID_SOCKET)
{
std::cout<<"Socket creation failed.rn";
WSACleanup();
system("PAUSE");
}

SOCKADDR_IN serverInf;
serverInf.sin_family=AF_INET;
serverInf.sin_addr.s_addr=INADDR_ANY;
serverInf.sin_port=htons(8888);

if(bind(Socket,(SOCKADDR*)(&serverInf),sizeof(serverInf))==SOCKET_ERROR)
{
std::cout<<"Unable to bind socket!rn";
WSACleanup();
system("PAUSE");
}

listen(Socket,3);

SOCKET TempSock=SOCKET_ERROR;
while(TempSock==SOCKET_ERROR)
{
std::cout<<"Waiting for incoming connections...rn";
Sleep(5000);
TempSock=accept(Socket,NULL,NULL);
}

// If iMode!=0, non-blocking mode is enabled.
u_long iMode=1;
ioctlsocket(Socket,FIONBIO,&iMode);

Socket=TempSock;
std::cout<<"Client connected!rnrn";

// Main loop
for(;;)
{
int nError=WSAGetLastError();
if(nError!=WSAEWOULDBLOCK&&nError!=0)
{
client_disconnected(Socket);
break;
}
char *szMessage="Welcome to the server!rn";
send(Socket,szMessage,strlen(szMessage),0);
Sleep(2000);
}
}
void client_disconnected(SOCKET Socket)
{
std::cout<<"Client disconnected!rn";

// Shutdown our socket
shutdown(Socket,SD_SEND);

// Close our socket entirely
closesocket(Socket);

WSACleanup();
}


int main()
{
//starting multiple threads for invoking server

std::thread threads[num_of_threads];
//This statement will launch multiple threads in loop
for (int i = 0; i < num_of_threads; ++i) {
threads[i] = std::thread(start_server);
Sleep(2000);
}

for (int i = 0; i < num_of_threads; ++i) {
threads[i].join();
}
return 0;

}


Моя Клиентская Программа:



#include <iostream>
#include <winsock2.h>
#include <Windows.h>
#include <process.h>
#include "client.h"
#pragma comment(lib,"ws2_32.lib")


void MultipleClient :: receiveToClient(void*data)
{
WSADATA WsaDat;
if(WSAStartup(MAKEWORD(2,2),&WsaDat)!=0)
{
std::cout<<"Winsock error - Winsock initialization failedrn";
WSACleanup();
system("PAUSE");

}

// Create our socket

SOCKET Socket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(Socket==INVALID_SOCKET)
{
std::cout<<"Winsock error - Socket creation Failed!rn";
WSACleanup();
system("PAUSE");

}

// Resolve IP address for hostname
struct hostent *host;
if((host=gethostbyname("localhost"))==NULL)
{
std::cout<<"Failed to resolve hostname.rn";
WSACleanup();
system("PAUSE");

}

// Setup our socket address structure
SOCKADDR_IN SockAddr;
SockAddr.sin_port=htons(8888);
SockAddr.sin_family=AF_INET;
SockAddr.sin_addr.s_addr=*((unsigned long*)host->h_addr);

// Attempt to connect to server
if(connect(Socket,(SOCKADDR*)(&SockAddr),sizeof(SockAddr))!=0)
{
std::cout<<"Failed to establish connection with serverrn";
WSACleanup();
system("PAUSE");

}

// If iMode!=0, non-blocking mode is enabled.
u_long iMode=1;
ioctlsocket(Socket,FIONBIO,&iMode);

// Main loop
for(;;)
{
// Display message from server
char buffer[1000];
memset(buffer,0,999);
int inDataLength=recv(Socket,buffer,1000,0);
std::cout<<buffer;

//end client when server is disconnected
int nError=WSAGetLastError();
if(nError!=WSAEWOULDBLOCK&&nError!=0)
{
std::cout<<"Winsock error code: "<<nError<<"rn";
std::cout<<"Server disconnected!rn";
// Shutdown our socket
shutdown(Socket,SD_SEND);

// Close our socket entirely
closesocket(Socket);

break;
}
Sleep(2000);
}

WSACleanup();
system("PAUSE");

}

class Client{
public:
static unsigned int __stdcall receiveMessageThread(void *p_this)
{
MultipleClient* mc = static_cast<MultipleClient*>(p_this);
mc-> receiveToClient(p_this); // Non-static member function!
return 0;
}
void startThread()
{
HANDLE myhandleA;
myhandleA = (HANDLE)_beginthreadex(0,0,&Client::receiveMessageThread,this,0, 0);
WaitForSingleObject(myhandleA, INFINITE);
}
};
int main(void)
{

Client *c = new Client;
c->startThread();

return 0;
}


Пожалуйста, помогите мне, как сделать несколько клиентов для подключения к одному серверу. Пример кода будет очень полезен (извините за вопрос).

1169   2  

2 ответов:

Ваш способ распределения работы между потоками неверен.

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

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

Теперь вы можете разделить работу: один поток возвращается к вызову listen на исходном сокете и ожидает новых соединений, в то время как другой поток захватывает новый сокет и выполняет необходимые операции ввода-вывода для взаимодействия с клиентом.

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

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

Каждый поток будет иметь сокет, который был назначен методом accept что-то вроде этого:

Сервер . Этот метод (run) выполняется в новом потоке. Я не показываю метод connectionRequest, чтобы быть практичным, но имейте в виду, что этот метод создает новый поток, который будет посещать удаленную конечную точку.

void SocketServer::run()
{
    int err = 0;

    err = initSocketServer();

    //Fallo el inicio de socket server.
    if (err != NO_ERROR)
    {
        stringstream out;
        out << "There was a problem to bind Native Port: " << _port;
        log->writeError(out.str());
        bKeepRunning = false;
    }
    else
        bKeepRunning = true;

    stringstream out;
    out << "Native Port stablished on port: " << _port;
    log->writeInfo(out.str());


    while (bKeepRunning)
    {
        stringstream out;
        SOCKET socket;

        socket = accept(server, NULL,NULL);

        if (bKeepRunning)
        {
            bool reuseadd = true;
            bool keepAlive = true;

            setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseadd, sizeof(bool));
            setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, (const char*)&keepAlive, sizeof(bool));


            //This method will create a new Thread wihc recives the socket.
            connectionRequest(socket);


            // It is ready to accept the nexto connection...
        }
    }

    serverStoped();
}

Посещение удаленного конца Точка . Новый поток, созданный запросом на подключение, должен присутствовать в удаленной конечной точке для получения отправки. Вот пример того, как выполнить другой

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

void SocketClient::run()
{
    stringstream out;
    bKeepRunning = true;
    bool wasClosed = false;
    int error = 0;
    error = 0;
    do
    {
        if(bSocketSet)
            log->writeDebug(_dlevel,"Socket Previamente asignado, No hay conexion");
        else
            log->writeDebug(_dlevel, "SIN Socket Previamente asignado, Se conectara");

        if(!bSocketSet) //If the socket has not been set
            bConnected = openConnection();

        if (bConnected)
        {

            if (!bSocketSet) //If the socket has not been set
            {
                out.str("");
                out.clear();
                out << "Connected to Server [" << _host << ":" << _port << "]";
                log->writeInfo(out.str());
            }

            //The readMessage will performed a loop to read data an report.
            error = readMessage(&wasClosed);

            if ((error != 0 && bKeepRunning) || wasClosed)
            {
                out.str("");
                out.clear();
                out << "There was an error on reading data. The connection colud be closed.";
                log->writeError(out.str());
                //if(!wasClosed)
                //closeConnection();
                bConnected = false;
            }

        } // if (bConnected) 
        else 
        {
            if (!bSocketSet)
            {
                out.str("");
                out.clear();
                out << "It was not possible to connect to Server [" << _host << ":" << _port << "]";
                log->writeError(out.str());
                waitForResume(15000); //Wait for 15 second to try to reconect
            }
        } //Else Not Connected

    } while (bKeepRunning && !bSocketSet); //do while The socket is not a Socket set by SocketServer

    if (bConnected)
        closeConnection();
}//SocketClient::run()

Каждым потоком, который посещает каждую удаленную конечную точку, вы можете отправить recive много данных, поскольку протокол устанавливает его, но в конце вы должны закрыть соединение, чтобы освободить сокет, который будет использоваться как можно скорее. ОС.

Comments

    Ничего не найдено.