每個用戶連接成功後,服務器創建兩個緊密協作的線程:
recv 監聽數據。SuspendThread),等待接收線程喚醒發送數據。兩線程同生共死:任一方異常或連接關閉,雙雙退出。雖簡化了邏輯,但每連接佔用兩線程,限制了最大併發數,需根據硬體資源評估。
因多線程共同操作 info_data,引入互斥量 CMutex m_mutex 保護臨界區:
// 接收線程片段
UINT RecvData(void* cs) {
SOCKET clientSocket = (SOCKET)cs;
while(1) {
numrcv = recv(clientSocket, buffer, MAXBUFLEN, 0);
buffer[numrcv] = '\0';
if(strcmp(buffer, "Close!") != 0) { // 普通訊息
// 遍歷 s_info 查找目標用戶
for(i=0; i<COUNT; i++) {
if(po != NULL) {
s1 = s_info.GetNext(po);
if(s1.pet.Compare(petname) == 0) {
m_mutex.Lock();
info_data.data = pos;
info_data.thread = s1.thread;
m_mutex.Unlock();
s1.thread->ResumeThread(); // 喚醒發送線程
break;
}
}
}
} else { // 用戶下線
// ... 類似加鎖操作,並從 s_info 移除用戶
}
}
closesocketlink((LPVOID)clientSocket);
AfxEndThread(0, true);
return 1;
}
// 發送線程片段
UINT SendData(void* cs) {
SOCKET clientSocket = (SOCKET)cs;
while(1) {
if(info_data.data != "Close!") {
m_mutex.Lock();
numsnd = send(clientSocket, info_data.data, info_data.data.GetLength(), 0);
now = info_data.thread;
m_mutex.Unlock();
now->SuspendThread(); // 發送完自我掛起
} else { goto bb; }
}
bb: closesocketlink((LPVOID)clientSocket);
AfxEndThread(0, true);
return 1;
}
客戶端雖無需處理複雜的多線程邏輯,但作為面向終端用戶的產品,體驗至上:
架構上劃分三個獨立對話框:註冊、登錄、主通信界面。註冊/登錄對接端口 56789,通信對接端口 56790,物理隔離了驗證與業務流量,兼顧安全與性能。
本系統已在實驗室局域網環境(1 台服務器,20 台客戶機)穩定運行。通過多線程與共享數據結構技術,實現了高效的局域網多用戶即時通訊,為基於 TCP/IP 的網絡應用開發提供了可參考的工程實踐。
參考文獻
[1] Eugene Olafsen 等. MFC Visual C++ 6.0 編程技術內幕. 機械工業出版社, 2000.
[2] Charles Wright. Visual C++ 程序員實用大全. 中國水利水電出版社, 2001.
作者:袁 淵(華東船舶工業學院 機械系,江蘇 鎮江 212003)