首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

MFC CSocket 服務端判斷 Socket 連接狀態的正確方法

     編輯:程式設計疑難解答

MFC CSocket 類封裝了 Windows Socket API,但在多客戶端服務端場景下,判斷某個 CSocket 對象是否已與客戶端建立有效連接,需結合狀態變量與非阻塞檢測,單純依賴對象指針無效。

核心判斷邏輯

CSocket 本身無 IsConnected() 方法。需自行維護連接狀態標誌,輔以 IOCTL 或心跳包檢測。

方案一:狀態標誌位 + Accept 回調(推薦)

// 自定義 Socket 類繼承 CSocket
class CClientSocket : public CSocket {
public:
    bool m_bConnected = false;      // 連接狀態標誌
    UINT m_nClientID = 0;           // 客戶端唯一標識
    
    virtual void OnAccept(int nErrorCode) override {
        if (nErrorCode == 0) {
            m_bConnected = true;
            // 通知主對話框/服務器有新連接
            GetParent()->PostMessage(WM_USER_NEW_CLIENT, (WPARAM)this);
        }
        CSocket::OnAccept(nErrorCode);
    }
    
    virtual void OnClose(int nErrorCode) override {
        m_bConnected = false;
        GetParent()->PostMessage(WM_USER_CLIENT_DISCONNECT, (WPARAM)this);
        CSocket::OnClose(nErrorCode);
    }
    
    virtual void OnReceive(int nErrorCode) override {
        // 接收數據邏輯
        CSocket::OnReceive(nErrorCode);
    }
};

// 服務端監聽 Socket
class CServerSocket : public CSocket {
public:
    virtual void OnAccept(int nErrorCode) override {
        if (nErrorCode == 0) {
            CClientSocket* pClient = new CClientSocket();
            if (Accept(*pClient)) {
                // 將 pClient 加入管理容器(如 CList)
                m_clientList.AddTail(pClient);
            } else {
                delete pClient;
            }
        }
        CSocket::OnAccept(nErrorCode);
    }
    
    CList m_clientList;
};

方案二:非阻塞模式檢測連接斷開


bool IsSocketConnected(CSocket& sock) {
    // 設置非阻塞
    u_long mode = 1; // 1 = 非阻塞
    sock.IOCTL(FIONBIO, &mode);
    
    char dummy;
    int nRet = sock.Receive(&dummy, 1, MSG_PEEK); // 偷窺模式,不移除數據
    
    // 恢復阻塞
    mode = 0;
    sock.IOCTL(FIONBIO, &mode);
    
    if (nRet == 0) return false;           // 优雅關閉 (FIN)
    if (nRet == SOCKET_ERROR) {
        int err = sock.GetLastError();
        if (err == WSAEWOULDBLOCK) return true; // 非阻塞無數據,連接正常
        return false;                       // 其他錯誤視為斷開
    }
    return true; // 有數據可讀,連接正常
}

方案三:應用層心跳機制(最可靠)

  • 客戶端每 30 秒發送 PING 封包
  • 服務端維護 lastHeartbeatTime,超過 60 秒無心跳判定斷開
  • 可檢測網絡中間設備(NAT/防火牆)靜默斷開的「半開連接」

最佳實踐總結

方法優點缺點適用場景
狀態標誌位簡單、無額外開銷無法檢測網絡層面斷開局域網、可靠連接
非阻塞 Peek能檢測 TCP FIN/RST需切換阻塞模式、輕微影響性能需實時性檢測
應用層心跳最可靠、穿透中間設備需協議支持、額外流量公網、長連接、生產環境

建議採用狀態標誌位作為基礎,心跳作為補充的組合策略,在 OnClose 回調中及時清理資源,構建健壯的多客戶端服務端。

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