简体   繁体   中英

How to implement multiple parallel connections with c++ using winsock2?

I am trying to implement a simple server, using c++ and winsock2 in VSC, that can accept and communicate with multiple clients at the same time. I tried to use multithreading and an array of client and it seems that I can connect to multiple clients but I can communicate only with one at a time. As soon as I close the connection the next client connects. I believe it has something to do with the join function of the thread. But I couldn´t find a way to solve it differently.

#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>
#include <thread>

#pragma comment(lib, "Ws2_32.lib")

#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "27015"
#define MAXIMUM_CONNECTIONS 10

int connectionsCount = 0;
SOCKET clients[MAXIMUM_CONNECTIONS];

using namespace std;

void communicatingWithClient(SOCKET*);
void listenForConnection(SOCKET*);

int main()
{

    WSADATA wsaData;
    int iResult;

    SOCKET ListenSocket = INVALID_SOCKET;

    struct addrinfo *result = NULL;
    struct addrinfo hints;

    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
    if (iResult != 0)
    {
        printf("WSAStartup failed with error: %d\n", iResult);
        WSACleanup();
        return 1;
    }
    printf("WSA started\n");
    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;

    // Resolve the local address and port to be used by the server
    iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
    if (iResult != 0)
    {
        printf("getaddrinfo failed: %d\n", iResult);
        freeaddrinfo(result);
        WSACleanup();
        return 1;
    }

    // Create a SOCKET for the server to listen for client connections
    ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    if (ListenSocket == INVALID_SOCKET)
    {
        printf("Error at socket(): %d\n", WSAGetLastError());
        freeaddrinfo(result);
        WSACleanup();
        return 1;
    }

    printf("ListenSocket created\n");
    // Setup the TCP listening socket
    iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
    if (iResult == SOCKET_ERROR)
    {
        printf("bind failed with error: %d\n", WSAGetLastError());
        freeaddrinfo(result);
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    freeaddrinfo(result);

    thread listenThread(listenForConnection, &ListenSocket);

    listenThread.join();

    closesocket(ListenSocket);
    WSACleanup();
    return 0;
}

void listenForConnection(SOCKET *ListenSocket)
{
    int iResult;
    // Listen on the ServerSocket forever
    while(true)
    {
        printf("While Loop for listening started...\n");
        iResult = listen( *ListenSocket, SOMAXCONN);
        if (iResult == SOCKET_ERROR )
        {
            printf( "Listen failed with error: %d\n", WSAGetLastError() );
            closesocket(*ListenSocket);
            WSACleanup();
            return;
        }
        printf("Accepting client socket..\n");
        // Accept a client socket
        if (connectionsCount < 10) 
        {
            clients[connectionsCount] = accept(*ListenSocket, NULL, NULL);
            if (clients[connectionsCount] == INVALID_SOCKET)
            {
                printf("accept failed: %d\n", WSAGetLastError());
                closesocket(*ListenSocket);
                closesocket(clients[connectionsCount]);
                WSACleanup();
                return;
            }
            printf("Client accepted\n");
        }
        else
        {
            continue;
        }
        // Receive until the peer shuts down the connection
        thread communicationThread(communicatingWithClient, &clients[connectionsCount++]);
        communicationThread.join();
    }
}

void communicatingWithClient(SOCKET *ClientSocket)
{
    int iResult;
    int iSendResult;
    char recvbuf[DEFAULT_BUFLEN];
    int recvbuflen = DEFAULT_BUFLEN;

    do
        {
            printf("Receiving bytes from client...\n");
            iResult = recv(*ClientSocket, recvbuf, recvbuflen, 0);
            if (iResult > 0)
            {
                printf("Bytes received: %d\n", iResult);

                // Echo the buffer back to the sender
                iSendResult = send(*ClientSocket, recvbuf, iResult, 0);
                if (iSendResult == SOCKET_ERROR)
                {
                    printf("send failed: %d\n", WSAGetLastError());
                    closesocket(*ClientSocket);
                    WSACleanup();
                    return;
                }
                printf("Bytes sent: %d\n", iSendResult);
            }
            else if (iResult == 0)
            {
                printf("Connection closing...\n");
            }
            else
            {
                printf("recv failed: %d\n", WSAGetLastError());
                closesocket(*ClientSocket);
                WSACleanup();
                return;
            }
        }
        while (iResult > 0);

        // shutdown the send half of the connection since no more data will be sent
        iResult = shutdown(*ClientSocket, SD_SEND);
        printf("ClientSocket shut down\n");
        if (iResult == SOCKET_ERROR)
        {
            printf("shutdown failed: %d\n", WSAGetLastError());
            closesocket(*ClientSocket);
            WSACleanup();
            return;
        }
        closesocket(*ClientSocket);
}

Your problem is caused by this part :

// Receive until the peer shuts down the connection
thread communicationThread(communicatingWithClient, &clients[connectionsCount++]);  
communicationThread.join();  

More precisely, by this call communicationThread.join(); If you wait until finish communication with a client, you will not be able to communicate with another one. (because you have to wait for the communication thread to finish before you can create a new one)

your code as it is written, is therefore sequential

The usefulness of multithreaind is when threads are allowed to run in parallel . If you synchronize immediately with each creation of a thread, you will not be able to use multithreading and your code can be replaced by a sequential one

for best performance you must use async sockets, select method (FD_WRITE, FD_READ, FD_CLOSE...). If you use one thread per socket (sync), sooner and later you will have overhead between threads if you manage thousand of connections, because the OS will be wasting CPU/cycles changing context (thread ID, stack variable...) than doing real task or work. You can't know when a thread will be doing real work, you don't know when a socket will have data (recv), but you can ask (select) if there is data pending, and as fast as you can, switch to another socket in the same thread. In the other hand, if you have data pending, other sockets must wait before you do 'recv' in the current socket in the same thread. There is no a one solution, depends on project, but you can create a pool of threads, and one thread can handle a dozen of connected sockets. If you have more physical cores (CPU), you can create more threads, but watch out with logical cores, they share physical components and you can have bad performance.

When you have already registers on the behavior of the project, you can optimize or learn and improve it. So, Big Data + Machine Learning is the next step.

This is the way most of frameworks work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM