首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

基於 IOCP 的局域網聊天軟體架構設計與實現

     編輯:關於C++

專案概覽

實現一款局域網即時通訊軟體,支援自動發現線上裝置、文字訊息收發。核心技術棧:Winsock2、IOCP (I/O Completion Port)、多執行緒同步、MFC GUI。開發週期 4 天,程式碼約 2000 行。

核心架構:三線程協作模型

核心類程式架構圖

線程職責關鍵 API
MainServerTCP 監聽、接受連線、建立 IOCP 關聯accept, CreateIoCompletionPort, WSARecv
UDPServerUDP 廣播上線通知、接收廣播、發起 TCP 連線sendto, recvfrom, connect
IOCPWorker (N個)非同步收發處理、訊息分發GetQueuedCompletionStatus, WSARecv, WSASend

關鍵流程:裝置發現與連線建立

  1. 啟動階段:MainServer `bind`/`listen` 完成後 `SetEvent` 通知 UDPServer
  2. 廣播上線:UDPServer 發送單字元 `"C"` 到 `255.255.255.255:4567`
  3. 接收廣播:線上裝置收到 UDP 封包,解析來源 IP,發起 TCP `connect`
  4. 雙向建連:雙方各自在 MainServer/UDPServer 執行緒中 `accept`/`connect` 成功
  5. 掛載 IOCP:新連線 `CreateIoCompletionPort` 關聯到完成端口,投遞首個 `WSARecv`
  6. 訊息迴路:IOCP 工作執行緒處理後續所有收發,完成後自動再投遞 `WSARecv`

核心類別設計:CLANChat

class CLANChat {
private:
    // 單例模式防拷貝
    CLANChat(CLANChat&);
    CLANChat& operator=(CLANChat&);
    
    // 靜態共享資料 (進程級單例)
    static std::vector<OnLineNode> g_OnLineList;   // 線上裝置列表
    static volatile HANDLE hIOCP;                  // IOCP 完成端口句柄
    static ULONG g_IPaddr;                         // 本機 IP
    static SOCKET sListen;                         // 監聽 Socket
    static sockaddr_in m_local_sa_in;              // 本機地址
    
    CDialog* m_pDlgFrame;                          // 主視窗指標 (用於 PostMessage)
    DlgIOCP** m_pDlgIOCPArray;                     // IOCP 工作執行緒參數陣列
    CEvent m_eMainUDPServer;                       // MainServer 啟動完成事件
    
public:
    CLANChat(CDialog* pDlg);
    ~CLANChat();
    
    // 公開介面
    int SendInfo(char* pbuf, int length, in_addr nListID);
    SOCKET GetSocketWithIP(in_addr addr);
    std::vector<OnLineNode>* GetOnLineListPtr();
    
    // 靜態執行緒入口
    static DWORD WINAPI MainServer(LPVOID lpParam);
    static DWORD WINAPI UDPServer(LPVOID lpParam);
    static DWORD WINAPI IOCPTCPSever(LPVOID lpParam);
};

IOCP 非同步收發核心邏輯

DWORD WINAPI CLANChat::IOCPTCPSever(LPVOID lpParam) {
    DlgIOCP* di = (DlgIOCP*)lpParam;
    HANDLE hCompletion = di->hIOCP;
    CDialog* pDlg = di->pDlg;
    delete di;  // 參數結構體用完即刪
    
    PMOV pOv;
    PIOCP_KEY pKey;
    DWORD bytes, flags;
    BOOL ok;
    CRITICAL_SECTION cs;
    InitializeCriticalSection(&cs);
    
    while (TRUE) {
        // 等待完成通知
        ok = GetQueuedCompletionStatus(hCompletion, &bytes, 
                                       (PULONG_PTR)&pKey, (LPOVERLAPPED*)&pOv, INFINITE);
        
        EnterCriticalSection(&cs);
        OnLineNode node; node.sClient = pKey->sClient;
        auto it = std::find(g_OnLineList.begin(), g_OnLineList.end(), node);
        
        if (!ok || bytes == 0) {  // 連線斷開或錯誤
            closesocket(pKey->sClient);
            if (it != g_OnLineList.end()) g_OnLineList.erase(it);
            PostMessage(pDlg->m_hWnd, WM_CLOSESOCKET, ...);
            GlobalFree(pKey); GlobalFree(pOv);
            LeaveCriticalSection(&cs);
            continue;
        }
        
        switch (pOv->OperateType) {
            case 0:  // 讀完成
                // 重新投遞下一次讀取
                WSARecv(pKey->sClient, &pOv->wbuf, 1, &bytes, &flags, 
                        (LPWSAOVERLAPPED)pOv, NULL);
                
                // 訊息分發:若聊天視窗已開啟直接送視窗,否則通知主視窗閃爍
                if (it->pDlg) 
                    PostMessage(it->pDlg->m_hWnd, WM_RECVMESSAGE, (DWORD)&(*it), 0);
                else
                    PostMessage(pDlg->m_hWnd, WM_RECVMESSAGE, ...);
                break;
            case 1: /* 寫完成 */ break;
        }
        LeaveCriticalSection(&cs);
    }
    return 0;
}

關鍵技術難點與解決

難點解決方案
多執行緒存取線上列表`CRITICAL_SECTION` 保護 `g_OnLineList` 所有增刪查操作
IOCP 完成通知參數傳遞自定義 `IOCP_KEY` 結構體攜帶 Socket、對端地址,`CreateIoCompletionPort` 第 3 參數傳遞
非同步接收緩衝區管理`MOV` 結構體內嵌 `WSABUF` 與 `OVERLAPPED`,完成後直接重用投遞
視窗句柄跨執行緒訊息傳遞`PostMessage` 線程安全,配合 `WM_RECVMESSAGE`/`WM_NEWSOCKET` 自定義訊息
優雅關閉與資源釋放析構函數清理 `m_pDlgIOCPArray`,連線斷開時 `GlobalFree` 釋放 `IOCP_KEY`/`MOV`

介面截圖

LANChat 主介面截圖

已知限制與改進方向

  • ❌ 析構函數未完整清理所有 Socket、記憶體 (開發初期疏忽)
  • ❌ 鍵盤訊息處理缺陷:空格/Enter 導致程式退出
  • ❌ 單一埠號硬編碼,無配置檔支援
  • ✅ 重構建議:引入 RAII 封裝 Socket/記憶體、使用 `std::thread` 替代 `CreateThread`、採用訊息佇列解耦網路層與 UI 層

心得體會

IOCP 是 Windows 高效能網路編程的基石。初學時覺得 `OVERLAPPED`、`CompletionKey`、`GetQueuedCompletionStatus` 概念繁雜,但實作一次後發現其邏輯極其清晰:發起非同步操作 → 核心完成後排入佇列 → 工作執行緒取出處理 → 發起下一個非同步操作。這種「主動物件」模式完美契合高併發場景,值得深入掌握。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved