Внедрение сервера на C winapi

Я новичок в многопоточности и хочу разработать сервер

это мой текущий код:

typedef struct Session
{
    HANDLE handlers[2];
    //HANDLE h_Send;
    //main session handler
    HANDLE h_MainHandler;
    HANDLE sema_MessageQ;

    char* UserName;
    SOCKET Socket;

    PList MessageQ;

}Session,*pSession; 
#define NUM_OF_WORKER_THREADS 5

#define MAX_LOOPS 5

#define SEND_STR_SIZE 256
static Session ClientSessions[NUM_OF_WORKER_THREADS];

void MainServer()
{
    int Ind;
    int Loop;
    int bindRes;
    int ListenRes;
    unsigned long Address;
    char SendStr[SEND_STR_SIZE];

    SOCKET MainSocket = INVALID_SOCKET;

    SOCKADDR_IN service;

    XPacket Packet;
    TransferResult_t RecvRes;
    TransferResult_t SendRes;

    //doing all the WSAStartup / bind / listen 
    //and error checkinh


    // Initialize all thread handles to NULL, to mark that they have not been initialized
    for ( Ind = 0; Ind < NUM_OF_WORKER_THREADS; Ind++ )
        ClientSessions[Ind].h_MainHandler = NULL;

    printf( "Waiting for a client to connect...\n" );

    for ( Loop = 0; Loop < MAX_LOOPS; Loop++ )
    {
        SOCKET AcceptSocket = accept( MainSocket, NULL, NULL );
        if ( AcceptSocket == INVALID_SOCKET )
        {
            printf( "Accepting connection with client failed, error %ld\n", WSAGetLastError()); 
            goto server_cleanup_3;
        }

        printf( "Client Connected.\n" );

        Ind = FindFirstUnusedThreadSlot();

        if ( Ind == NUM_OF_WORKER_THREADS ) //no slot is available
        { 
            printf( "No slots available for client, dropping the connection.\n" );
            closesocket( AcceptSocket ); //Closing the socket, dropping the connection.
            continue;
        } 
        else    
        {
            /*check that user name is not in use*/
            char *AcceptedStr = NULL;
            //get UserName
            RecvRes = ReceivePacket(&Packet, AcceptSocket);

            if ( RecvRes == TRNS_FAILED || RecvRes == TRNS_DISCONNECTED )
            {
                printf( "Service socket error while reading, closing socket.\n" );
                closesocket( AcceptSocket );
                continue;
            }
            if (FALSE == CheckForUsedName((char*)Packet.Data))
            {
                strcpy(SendStr ,(char*)Packet.Data);
                strcpy( SendStr + strlen((char*)Packet.Data), " already taken!" );
                SendRes = (TransferResult_t)PROTOCOL_SendCommand(
                        SendStr,
                        strlen(SendStr) + 1,
                        PROT_SERVER_RECEIVE_SYSTEM_MSG,
                        AcceptSocket);
                closesocket(AcceptSocket);
                continue;
            }

            printf("\r\nCode:%d Length:%lu Text:%s",Packet.Code,Packet.Length,(char*)Packet.Data);

            ClientSessions[Ind].UserName = (char*)Packet.Data;
            ClientSessions[Ind].Socket = AcceptSocket;


            ClientSessions[Ind].h_MainHandler = CreateThread(
                NULL,
                0,
                ( LPTHREAD_START_ROUTINE ) ServiceThread,
                &( ClientSessions[Ind]),
                0,
                NULL
            );
        }
    } // for ( Loop = 0; Loop < MAX_LOOPS; Loop++ )

//~~~~~~~~server clean up~~~~~~~~~~~ 
server_cleanup_3:
    CleanupWorkerThreads();

server_cleanup_2:
    if ( closesocket( MainSocket ) == SOCKET_ERROR )
        printf("Failed to close MainSocket, error %ld. Ending program\n", WSAGetLastError() ); 

server_cleanup_1:
    if ( WSACleanup() == SOCKET_ERROR )     
        printf("Failed to close Winsocket, error %ld. Ending program.\n", WSAGetLastError() );
}

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

сервер:

{
    //accept first client
    \\while( InterlockedAdd(&number_of_connected_client , 0) >0 ) ?
    while(number_of_connected_client > 0)        
    {
      //wait for new client
    }

    //close server
}

каждый сеанс клиента:

{

    //do somthing
    //once finish
    InterlockedDecrement(number_of_connected_client );

}

мой вопрос заключается в том, как реализовать while() часть не в spin-lock

1 ответ

Дизайн сервера, который я использовал ранее, очень похож на тот, который у вас уже есть.

Поток основного сервера ожидает подключения от клиентов (просто измените цикл for, чтобы сделать его бесконечным циклом (например, while(1) { // принять подключение и т. Д. }). Когда основной поток принимает подключение, он проходит через сокет (возвращается через accept) в рабочий поток.

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

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

Я создал сервер много лет назад (когда я не знал ничего лучше), и в предложенном мною решении использовались функции синхронизации Windows. Я использовал Windows Events для координации между основным потоком и рабочим потоком. Я не буду вдаваться в подробности, потому что я никогда не буду делать это снова. Это было слишком сложно. Я делал все вручную.

Если бы мне пришлось сделать это сегодня, я бы просто использовал пул потоков Windows (см. Функцию QueueUserWorkItem).

Другие вопросы по тегам