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

C++ 異步 Socket WSAEventSelect 模型中 FD_WRITE 事件的正確理解與使用

     編輯:程式設計綜合問答

常見誤區

許多開發者以為 FD_WRITE 表示「可以發送數據」的通知,於是嘗試在收到該事件時調用 send()。實際上這會導致邏輯混亂甚至發送失敗。

FD_WRITE 真實觸發條件

  • 連接建立成功時(TCP 三次握手完成),發送緩衝區為空,**首次**觸發 FD_WRITE。
  • 發送緩衝區從「滿/不可寫」變為「可寫」時**,即發送窗口滑動、對端確認數據後,內核通知應用層「現在可以繼續寫入」。

正確的發送策略

  1. 想發就發:應用層維護發送隊列,有數據時直接調用 send(),**無需等待 FD_WRITE**。
  2. 處理 WSAEWOULDBLOCK:若 send() 返回該錯誤,說明發送緩衝區滿,將數據暫存到應用層發送隊列,等待下一次 FD_WRITE 觸發後再嘗試發送隊列數據。
  3. FD_WRITE 作為「繼續發送」信號:收到事件時,遍歷發送隊列盡可能多地 send(),直到隊列空或再次 WSAEWOULDBLOCK。

代碼框架

std::queue> sendQueue;

void OnSendRequest(const char* data, int len) {
    sendQueue.emplace(data, data + len);
    TrySend();
}

void TrySend() {
    while (!sendQueue.empty()) {
        auto& buf = sendQueue.front();
        int ret = send(socket, buf.data(), buf.size(), 0);
        if (ret > 0) {
            if (ret == buf.size()) sendQueue.pop();
            else { buf.erase(buf.begin(), buf.begin() + ret); }
        } else if (WSAGetLastError() == WSAEWOULDBLOCK) {
            break; // 等待 FD_WRITE
        } else {
            // 錯誤處理
            break;
        }
    }
}

void OnEventSelect(WSAEVENT event) {
    WSANETWORKEVENTS netEvents;
    WSAEnumNetworkEvents(socket, event, &netEvents);
    if (netEvents.lNetworkEvents & FD_WRITE) {
        TrySend(); // 緩衝區可寫了,繼續發送隊列
    }
    // FD_READ, FD_CLOSE 等其他處理...
}

總結

  • FD_WRITE ≠ 發送觸發器,它是「緩衝區可寫」的**通知**。
  • 主動發送調用 send(),遇阻塞入隊,FD_WRITE 來了再清隊。
  • 小數據、低頻場景甚至完全可以忽略 FD_WRITE,直接同步 send() 或用 select/poll/epoll/IOCP 更現代的模型。

理解內核發送緩衝區機制,才能駕馭事件選擇模型,避免「想用 FD_WRITE 發數據卻只收到一次事件」的困境。

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