C winapi中服务器的实现

Posted

技术标签:

【中文标题】C winapi中服务器的实现【英文标题】:Implementing of server in C winapi 【发布时间】:2016-01-05 15:08:42 【问题描述】:

我是多线程新手,想设计服务器

这是我当前的代码:

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 );


我的问题是如何实现不在spin-lock中的while()部分

【问题讨论】:

在我看来,您不应该在这里使用无锁方法。你只会以一场比赛告终。使用锁。通过该锁保护客户端的连接和断开连接。当客户端计数变为零时,发出终止信号。也许通过发出一个主线程阻塞的事件。当主线程检测到事件发出信号时,它会关闭。 @DavidHeffernan 我不确定我是否理解,您能否发布一些代码或链接到更完整的示例 我没有时间。你知道多线程中的事件是什么吗? 【参考方案1】:

我之前使用的服务器设计与您已有的非常相似。

主服务器线程正在等待来自客户端的连接(只需更改您的 for 循环以使其成为无限循环(例如 while(1) //accept connection etc. )。当主线程接受连接时,它将套接字(由接受返回)传递给工作线程。

这个设计的棘手部分是管理工作线程,以某种方式为每个客户端连接选择一个工作线程,并将套接字传递给所选的工作线程。

我注意到您为每个连接创建了一个新的工作线程。这样做的好处是简单,当然缺点是为每个连接创建一个线程会影响性能。

我多年前创建了一个服务器(当时我不知道更好),我想出的解决方案使用了 Windows Synchronization Functions。我使用 Windows 事件在主线程和工作线程之间进行协调。我不会详细介绍,因为我再也不会那样做了。这太复杂了。我基本上都是手动做的。

如果我今天必须这样做,我只会使用 Windows 线程池(请参阅QueueUserWorkItem 函数)。

【讨论】:

以上是关于C winapi中服务器的实现的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 WinAPI 和 C++ 从 Windows 系统上的服务通知客户端应用程序?

如何使用 C WinAPI 获取当前麦克风输入电平?

SendMessage() WINAPI 在用于连接到 DDE 服务器时挂起

如何在c ++ winapi中获取活动文件资源管理器窗口的路径

DWORD WINAPI?stdcall?

在 C++/WinAPI 程序中读取 Trackbar 控件值