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

| 線程 | 職責 | 關鍵 API |
|---|---|---|
| MainServer | TCP 監聽、接受連線、建立 IOCP 關聯 | accept, CreateIoCompletionPort, WSARecv |
| UDPServer | UDP 廣播上線通知、接收廣播、發起 TCP 連線 | sendto, recvfrom, connect |
| IOCPWorker (N個) | 非同步收發處理、訊息分發 | GetQueuedCompletionStatus, WSARecv, WSASend |
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);
};
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` |

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