Win32 multithreaded sockets

381 views Asked by At

I have a server class that has a method called handle_client as follows:

void server::handle_client()
{
    do {
      // Accept a client socket
      EnterCriticalSection(&listenSocketCriticalSection);
      SOCKET clientSocket = accept(listenSocket, NULL, NULL);
      LeaveCriticalSection(&listenSocketCriticalSection);

      // ... rest of the client handling code that reads
      // from the client socket and sends appropriate response
      // ...
    } while(true);
}

I have a run method as follows:

void server::run()
{
    // create the threads
    for (int i = 0; i < THREAD_POOL_SIZE; i++) {
        DWORD dwThreadId;
        thread_pool_handle[i] = CreateThread(NULL, 0, thread_function, this, 0, &dwThreadId);
    }

    WaitForMultipleObjects(THREAD_POOL_SIZE, thread_pool_handle, true, INFINITE);
}

I have a thread_function as follows:

DWORD WINAPI thread_function(LPVOID lpParam)
{
    server* pServer = (server*)lpParam;
    pServer->handle_client();
}

I am creating a pool of threads that are all waiting for a client socket connection to be accepted. Since I have wrapped the accept within a critical section, only one thread will succeed at a time. Once a client socket is accepted by the server thread, that thread continues to go on to handle the request. The idea is that the thread will loop back indefinitely to the accept call after completing a request.

Questions:

  1. Is the Critical Section necessary? (I think so, because otherwise the accept call from the multiple threads on the same listenSocket would clobber things. Is this correct?)
  2. If handle_client loops indefinitely, what is the best way to cleanly terminate all the threads and exit the server? Should I use a special message from the client to trigger the thread terminations? Any other suggestions?
  3. How should I handle the server process termination gracefully (as it pertains to the thread pool)?
1

There are 1 answers

2
Torrecto - MSFT On

It Is recommended to use Select model to store socket objects in multithreaded sockets. In Select model, you can use FD_CLR() to clear sockets when there are no network events.

I have the code for the server with the select socket, You can try to run and modify.

#include <iostream>
#include<WinSock2.h>
#include<windows.h>
#include<WS2tcpip.h>
#pragma comment(lib,"ws2_32.lib")
#pragma warning(disable:4996)//inet_addr
int main()
{
    //1.Obtain version info
    WSADATA wsaData = { 0 };
    SOCKET hServer = { 0 };
    WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
    {
        printf("version failed %d\n", GetLastError());
        return -1;
    }
    else
    {
        printf("version succeed \n");
    }
    //2.create socket
    hServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (hServer == SOCKET_ERROR)
    {
        printf("create socket tcp failed %d\n", GetLastError());
        return -1;
    }
    else
    {
        printf("create socket tcp succeed \n");
    }
    //3. Create a protocol address family
    sockaddr_in ServerAddr = { 0 };
    ServerAddr.sin_family = AF_INET6;
    ServerAddr.sin_zero[8];
    ServerAddr.sin_port = htons(8888);
    ServerAddr.sin_addr.S_un.S_addr = inet_addr("192.168.2.50");;//modify your address

    //4.bind
    int nRet = bind(hServer, (sockaddr*)&ServerAddr, sizeof ServerAddr);
    if (nRet == SOCKET_ERROR)
    {
        printf("bind failed %d\n", GetLastError());
        closesocket(hServer);
        WSACleanup();
        return -1;
    }
    else
    {
        printf("bind succeed \n");
    }
    //5.listen
    nRet = listen(hServer, 3);
    if (nRet == SOCKET_ERROR)
    {
        printf("listen failed %d\n", GetLastError());
        closesocket(hServer);
        WSACleanup();
        return -1;
    }
    else
    {
        printf("listen succeed \n");
    }
    sockaddr_in clientAddr = { 0 };// The protocol address family used to receive the client
    int len = sizeof(clientAddr);// The size of the accepted client protocol address family information
    // Create a select model to store socket objects
    FD_SET fd_read;
    FD_ZERO(&fd_read);
    FD_SET(hServer, &fd_read);

    //6. Accept client connections
    while (1) {
        FD_SET fd_tmp = fd_read;// Read backup can only be in 
        const timeval tv = { 1,0 };
        int Ret = select(NULL, &fd_tmp, NULL, NULL, &tv);
        if (Ret == 0) // No network events, TMP is automatically deleted       
     {
            Sleep(1000);
            continue;
        }
        for (int i = 0; i < fd_tmp.fd_count; i++)
        {
            // If there are network events for a listening socket, it proves that a client is connecting to the socket
            if (fd_tmp.fd_array[i] == hServer)
            {
                SOCKET hclient;
                hclient = accept(hServer, (sockaddr*)&clientAddr, &len);// If you do not want to store the protocol address family information of the client, you can pass a NULL address
                if (hclient == SOCKET_ERROR)
                {
                    printf("recieve information of client failed %d\n", GetLastError());
                    closesocket(hServer);
                    return -1;
                }
                printf("connecting: %s******** \n", inet_ntoa(clientAddr.sin_addr));
                FD_SET(hclient, &fd_read);
            }
            else // The client socket has network events that prove that the client is sending data and the server is accepting the data
            {
                char buff[32] = { 0 };
                int nRet = recv(fd_tmp.fd_array[i], (char*)buff, 32, NULL);
                if (nRet > 0)
                {

                    printf("message: %s\n", buff);
                }
                else// Removes the current socket fd_array from the fd_read
                {
                    FD_CLR(fd_tmp.fd_array[i], &fd_read);
                    printf("Disconnect \n", GetLastError());
                    closesocket(hServer);
                    closesocket(fd_read.fd_array[i]);
                    break;

                }

            }
            break;
        }

       
    }
    //7.close socket
    WSACleanup();
    getchar();
    return 0;
}