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

MFC UDP 伺服器/客戶端雙向通訊實現:執行緒與 Socket 非阻塞模式

     編輯:VC 教學

基於 MFC 的 UDP 雙向通訊完整架構解析

本文解析一個 MFC 單文件介面 (SDI) 應用程式,實現 UDP 伺服器端監聽、資料接收、顯示與回傳的完整流程。涉及 Winsock 初始化、Socket 建立/繫結、背景執行緒監聽、訊息泵整合、UI 更新同步等關鍵技術。

核心類別成員設計 (CServerView)

// 通訊相關
SOCKET m_sock;                    // UDP Socket
HANDLE m_hListenThread;           // 監聽執行緒句柄
BOOL m_bInitialized;              // Winsock 初始化旗標
WSADATA WSAData;                  // Winsock 資料結構
BOOL flag;                        // 通用旗標
SOCKADDR_IN saClnt;               // 客戶端位址
int saClntLen;                    // 位址結構長度
BOOL Isconnect;                   // 連線狀態 (UDP 無連線,此處語意為「已就緒」)

// UI 相關
CSize sizeTotal;                  // 捲軸範圍
int count;                        // 訊息計數
CString m_data[1000];             // 訊息緩衝陣列 (固定大小,生產環境建議用 CList/CArray)
char Hostname[260];               // 主機名
char Hostaddress[20];             // 主機 IP

建構子初始化流程 (CServerView::CServerView)

  1. Winsock 啟動:WSAStartup(0x0101, &wsaData) 要求 1.1 版本
  2. 建立 Socket:socket(AF_INET, SOCK_DGRAM, 0) 資料報模式
  3. 繫結埠口:bind(m_sock, (sockaddr*)&sa, sizeof(sa)) 綁定 INADDR_ANY:5050
  4. 啟動監聽執行緒:CreateThread(NULL, 0, Listen, this, 0, &idThread)

錯誤處理:每步驟失敗時記錄 m_data 並清理已分配資源。

監聽執行緒函數

DWORD WINAPI Listen(LPVOID lpParam) {
    CServerView* pView = (CServerView*)lpParam;
    char buffer[1024];
    int nLen = sizeof(pView->saClnt);
    
    while (pView->Isconnect) {
        int nRecv = recvfrom(pView->m_sock, buffer, sizeof(buffer), 0,
                             (sockaddr*)&pView->saClnt, &nLen);
        if (nRecv > 0) {
            buffer[nRecv] = '\0';
            CString msg = "Received: " + CString(buffer);
            
            // 執行緒安全更新 UI:PostMessage 而非直接呼叫
            pView->PostMessage(WM_USER_RECEIVED, 0, (LPARAM)new CString(msg));
            
            // 回傳 Echo
            sendto(pView->m_sock, buffer, nRecv, 0,
                   (sockaddr*)&pView->saClnt, nLen);
        }
    }
    return 0;
}

關鍵點:

  • UDP 無連線,recvfrom 同時取得資料與發送者位址
  • 執行緒不直接操作 UI,透過 PostMessage 送自定義訊息 (WM_USER_RECEIVED) 至主執行緒
  • 動態分配 CString* 傳遞訊息內容,接收端負責 delete

訊息處理與 UI 更新

// 訊息映射
ON_MESSAGE(WM_USER_RECEIVED, OnReceived)

// 處理函數
LRESULT CServerView::OnReceived(WPARAM, LPARAM lParam) {
    CString* pMsg = (CString*)lParam;
    m_data[count++] = *pMsg;
    delete pMsg;
    // 更新 ListBox/滾動顯示...
    return 0;
}

資源清理 (析構函數/OnDestroy)

Isconnect = FALSE;              // 通知執行緒退出
if (m_hListenThread) {
    WaitForSingleObject(m_hListenThread, 2000); // 等待執行緒結束
    CloseHandle(m_hListenThread);
}
if (m_sock != INVALID_SOCKET) closesocket(m_sock);
if (m_bInitialized) WSACleanup();

架構優化建議

  1. 訊息緩衝:固定陣列 m_data[1000] 易溢位,改用 CList<CString> 或循環緩衝區
  2. 錯誤碼處理:WSAGetLastError() 區分 WSAEWOULDBLOCK (非阻塞無資料) 與真正錯誤
  3. 封包大小:UDP 理論最大 65507 位元組,實務建議 ≤ 1472 (避免 IP 分片)
  4. 可靠性增強:應用層加入序號、ACK、重傳機制 (如 RUDP/KCP)
  5. 多客戶端:維護客戶端位址表,支援廣播/多播
  6. IPv6 相容:使用 sockaddr_storage、getaddrinfo 替代硬編碼 IPv4 結構

客戶端對應實現要點

  • socket(AF_INET, SOCK_DGRAM, 0) 建立 Socket
  • sendto 直接發送至伺服器 IP:Port
  • recvfrom 接收回傳資料
  • 可選 bind 固定本地埠口 (穿透 NAT 需固定埠)

此架構展示 MFC 視窗應用整合 Winsock 非阻塞通訊的標準範式,可延伸至 TCP、SSL/TLS、自定義協定等場景。

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