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

libcurl 多介面處理訂閱型 URL 持續數據流:curl_multi_perform 死循環問題解決方案

     編輯:關於C語言

libcurl 多介面處理訂閱型 URL 持續數據流:curl_multi_perform 死循環問題解決方案

使用 curl_multi_perform 並發請求多個**長連接/訂閱型** URL (如 WebSocket、Server-Sent Events、長輪詢) 時,常規「跑完再解析」模式會陷入死循環,因為 easy handle 永遠不會完成 (CURLMSG_DONE 不會產生)。本文分析問題根因,提供三種解決思路並指出最佳實踐方向。

問題場景

while (running_handles) {
    curl_multi_select(multi, &timeout);
    while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(multi, &running_handles));
    // 此處永遠執行不到,因為 running_handles 永遠 > 0
}
curl_multi_info_read(...); // 無法走到這裡解析數據

訂閱型連接**永不主動關閉**,服務器持續推送數據 (甚至心跳幀),導致 easy handle 始終處於「執行中」狀態。

方案一:將 curl_multi_info_read 移入循環 (無效)

while (running_handles) {
    curl_multi_select(...);
    curl_multi_perform(...);
    // 嘗試讀取完成信息
    while ((msg = curl_multi_info_read(multi, &left))) {
        if (msg->msg == CURLMSG_DONE) { /* 處理 */ }
    }
}

結果:無效。訂閱型 handle 永不產生 CURLMSG_DONE,循環體永不執行。

方案二:curl_easy_setopt 設置超時/範圍限制 (官方文檔無直接支持)

  • CURLOPT_TIMEOUT_MS:整體超時,**連接會被強制斷開**,不適合長連接;
  • CURLOPT_RANGE:斷點續傳,收滿指定字節後斷開,**後續數據丟失**;
  • 結論:libcurl 易介面無「接收超時自動回調」或「流式分幀回調」的內建選項。

方案三:輪詢檢查接收緩衝區 (可行但笨拙)

while (running_handles) {
    curl_multi_select(...);
    curl_multi_perform(...);
    // 主動掃描所有 handle 的接收緩衝區
    for (int i = 0; i < MAX_HANDLES; i++) {
        if (recv_buf[i][0] != '\0') { // 有新數據
            process_data(recv_buf[i]);
            memset(recv_buf[i], 0, sizeof(recv_buf[i]));
        }
    }
}

缺點:

  1. O(N) 全掃描,效率低;
  2. 無法感知某個 handle 異常斷開 (需額外心跳/錯誤碼檢測);
  3. 緩衝區管理複雜 (溢出、殘留、線程安全)。

最佳實踐方向:回調函數 + 狀態機

libcurl 設計初衷是**回調驅動**而非輪詢。正確模式:

  1. 為每個 easy handle 設置 CURLOPT_WRITEFUNCTION 寫入回調;
  2. 回調中**即時處理**收到的數據幀 (解析協議、分發業務邏輯);
  3. 回調返回實際處理字節數,libcurl 內部管理緩衝區;
  4. 主循環僅負責 curl_multi_select + curl_multi_perform 驅動事件輪詢;
  5. 若需關閉特定 handle:在回調或業務邏輯中調用 curl_multi_remove_handle,再 curl_easy_cleanup。

寫入回調原型

size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata) {
    size_t total = size * nmemb;
    // userdata 可傳入上下文結構體 (協議解析器、業務隊列等)
    parse_and_dispatch(ptr, total, userdata);
    return total; // 必須返回 total,否則 libcurl 認為出錯並中斷
}

設置回調

curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(easy, CURLOPT_WRITEDATA, &context); // 傳遞上下文

總結

方案可行性優點缺點
移入 curl_multi_info_read❌-訂閱型無 DONE 信息
超時/範圍選項❌-破壞長連接語義
輪詢緩衝區⚠️ 可行簡單直觀效率低、狀態感知弱
寫入回調 (推薦)✅ 最佳即時處理、零拷貝、狀態可控需重構為回調思維

**核心心法**:libcurl 多介面是**事件驅動**框架,**不要試圖同步輪詢**,擁抱回調,在 WRITEFUNCTION 中完成流式數據的幀解析與業務分發,才是處理持續數據流的正確姿勢。

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