socket模型處理多個客戶端


最近學完了簡單的socket編程,發現其實socket的網絡編程其實並沒有什么難度,只是簡單的函數調用,記住客戶端與服務端的步驟,寫起來基本沒有什么問題。
在服務器程序的設計中,一個服務器不可能只相應一個客戶端的鏈接,為了響應多個客戶端的鏈接,需要使用多線程的方式,每當有一個客戶端連接進來,我們就開辟一個線程,用來處理雙方的交互(主要是利用recv或者recvfrom用於收發信息),由於但是在網絡中可能出現這樣一種情況:由於處理比較復雜,下一條信息到來之后,上一條信息的處理還沒有完成,這樣信息太多了之后系統的緩沖占滿之后可能會發生丟包的現象,所以為了解決這個問題,需要另外再開一個線程,專門用來處理接收到的數據,這樣總共至少有3個線程,主線程,收發信息的線程,處理線程;這樣可能也不完整,處理的操作種類多了的話可能需要根據不同的請求來開辟不同的線程用來處理這一類請求,下面是實現這一思路的部分代碼:
全局變量:

DWORD WINAPI AcceptThread(LPVOID lpParameter);
DWORD WINAPI RecvThread(LPVOID lpParameter);

DWORD g_nAcceptID = 123;
DWORD g_nRecvID = 234;

HANDLE g_hAccpetThread;
HANDLE g_hRecvThread;

主線程函數:

int _tmain(int argc, _TCHAR* argv[])
{
    WSADATA wd;
    WSAStartup(MAKEWORD(2, 2), &wd);

    SOCKET sockListen = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
    if (INVALID_SOCKET == sockListen)
    {
        cout << "創建偵聽套接字失敗,錯誤碼為:" << WSAGetLastError() << endl;
        return -1;
    }

    SOCKADDR_IN srvAddr = { 0 };
    srvAddr.sin_family = AF_INET;
    srvAddr.sin_port = htons(6666);
    srvAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    if (SOCKET_ERROR == bind(sockListen, (SOCKADDR*)&srvAddr, sizeof(SOCKADDR)))
    {
        cout << "綁定失敗,錯誤碼為:" << WSAGetLastError() << endl;
        WSACleanup();
        return -1;
    }

    if (SOCKET_ERROR == listen(sockListen, 5))
    {
        cout << "偵聽失敗,錯誤碼為:" << WSAGetLastError() << endl;
        WSACleanup();
        return -1;
    }

    while (true)
    {
        SOCKET sockConn = accept(sockListen, NULL, 0);
        if (INVALID_SOCKET == sockConn)
        {
            cout << "本次連接失敗,即將進入下一次連接,錯誤碼為:" << WSAGetLastError() << endl;
            closesocket(sockConn);
            closesocket(sockListen);
            WSACleanup();
            continue;
        }

        g_hAccpetThread = CreateThread(NULL, 0, AcceptThread, &sockConn, 0, &g_nAcceptID);
    }

    WaitForSingleObject(g_hAccpetThread, INFINITE);
    WSACleanup();
    return 0;
}

收發數據函數:

DWORD WINAPI AcceptThread(LPVOID lpParameter)
{
    cout << "有客戶端連接進來" << endl;
    SOCKET sockConn = *(SOCKET*)lpParameter;
    while (true)
    {
        char *pszBuf = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY | HEAP_NO_SERIALIZE, 255);
        if (SOCKET_ERROR == recv(sockConn, pszBuf, 255, 0))
        {
            cout << "接受數據失敗,錯誤碼為:" << WSAGetLastError() << endl;
            cout << "准備進行下一次接受數據....." << endl;
            continue;
        }

        g_hRecvThread = CreateThread(NULL, 0, RecvThread, pszBuf, 0, &g_nRecvID);
        WaitForSingleObject(g_hRecvThread, INFINITE);
        if (0 == strcmp("exit", pszBuf))
        {
            cout << "正在斷開與該客戶端的連接" << endl;
            HeapFree(GetProcessHeap(), 255, pszBuf);
            return 0;
        }
    }

    return 0;
}

信息處理子線程:
DWORD WINAPI RecvThread(LPVOID lpParameter)
{
cout << “接受到客戶端的數據:” << (char*)lpParameter << endl;
return 0;
}
雖說這個解決了多個客戶端與服務器通信的問題,但是這樣寫確定也很明顯:所有的與客戶端通信的socket都有程序員自己管理,無疑加重了程序員的負擔;每有一個連接都需要創建一個線程,當有大量的客戶端連接進來開辟的線程數是非常多的,線程是非常耗資源的,所以為了解決這些問題就提出了異步的I/O模型,它們解決了這些問題,由系統管理套接字,不要要人為的一個個管理,同時不需要開辟多個線程來處理與客戶端的連接,我們可以將線程主要用於處理客戶端的請求上;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM