CppCon 2014 學習第2天:Using Web Services in C++

概述

這是一個會議或演講的概述內容,主要介紹一個關于C++ Rest SDK的分享,翻譯和理解如下:


翻譯

概述

  • 先介紹什么是典型的Web服務結構和它的特征
  • 講講調用這些Web服務的幾種方式
  • 重點介紹自己團隊開發的一個C++庫(C++ Rest SDK),這個庫幫助程序員方便地使用REST API
  • 會通過示例代碼講解這個庫怎么用,還會演示實際操作
  • 假設聽眾已經對HTTP協議、JSON格式、REST架構風格等網絡基礎知識有所了解,不做基礎普及

到底什么是網web service

那network service又是什么?

Remote Procedure Call

這段內容是在解釋“網絡服務”或“Web 服務”的核心概念。我們逐句來理解:


RFC 707 – “基于網絡的資源共享的高級框架”

RFC(Request for Comments)是互聯網標準文檔,RFC 707 定義了一種高層次的架構,用于在網絡中共享資源。

最簡單地說,就是在另一臺機器上執行一個函數并獲取結果

這說明網絡服務的核心功能是遠程調用:客戶端通過網絡“調用”服務器上的函數,服務器執行后返回結果。

服務器向客戶端提供一組可調用的操作

這就像一個遠程 API,客戶端只需要“調用”,不關心細節。

使用類似本地函數調用的方式,屏蔽底層細節,例如:
  • 參數的編碼與解碼格式
  • 底層傳輸協議(如 HTTP、TCP、UDP 等)

總結:

網絡服務的目標是讓客戶端像調用本地函數一樣調用遠程服務,而無需關心底層的網絡傳輸、參數封裝等技術細節。這是構建 REST、RPC、GraphQL 等系統的基礎思想。

可以類比為:

你寫了一個函數 add(a, b),本來是本地調用,但通過網絡服務框架,你可以在別的機器上調用這個函數,像本地一樣用 add(2, 3) 得到結果 5,而所有底層細節都被封裝隱藏了。

What is a web service?


Web 服務是一種軟件系統,旨在支持機器與機器之間通過網絡進行互操作。

也就是說,Web 服務的核心目的,是讓不同的計算機系統能夠自動互相調用和交流數據,不需要人為干預。這種“機器對機器”的通信,是現代系統集成的基礎。


簡單來說,Web 服務就是讓兩臺計算機通過互聯網進行通信的方式。

可以理解為一個通道:A 電腦通過某種標準(比如 HTTP + JSON)向 B 電腦發請求,B 執行處理并返回結果。整個流程就像你給一個人發消息,他看完回復你,但實際上是兩個程序在交互。


Web 服務目前有兩種常見的形式:SOAP 和 REST。

  • SOAP(Simple Object Access Protocol):早期使用 XML 的一種復雜通信協議,強調嚴格的格式和標準。
  • REST(Representational State Transfer):更輕量級,通常基于 HTTP + JSON,靈活易用,是現在主流方式。

雖然“Web 服務”這個詞最初常常與 SOAP 搭配使用,但現在不局限于此。

也就是說,很多人一說“Web 服務”,第一反應還是 SOAP,但實際上現代大多數系統都采用 REST 或其他形式的服務了。


這次討論主要關注 REST 風格的服務,也就是通過 HTTP 提供的 Web API。

比如你訪問 https://api.example.com/users/123 獲取用戶數據,這種就是典型的 REST 風格服務。它利用 HTTP 的 GET/POST/PUT/DELETE 等方法實現資源的操作,簡潔清晰,廣泛使用于移動應用、網頁、后端服務之間。


總結:

這段內容的重點在于說明:

  • Web 服務是計算機之間的標準通信機制
  • 主要有 SOAP 和 REST 兩種風格
  • 當前主流是基于 HTTP 的 REST API
  • 我們要討論的重點,就是如何構建和使用這種 REST 式的 Web 接口

REpresentational State Transfer(REST)

表述性狀態轉移


分詞解釋:

  • Representational(表述性)
    指的是資源的“表現形式”或“表現層”。在 REST 中,客戶端不會直接操作服務器上的資源本身,而是通過資源的表現形式(比如 JSON、XML)進行交互。

  • State(狀態)
    指的是資源在某一時刻的狀態。比如一個用戶對象 { "name": "Alice", "age": 25 } 就代表了這個用戶在當前的狀態。

  • Transfer(轉移)
    表示客戶端和服務器之間通過 HTTP 協議傳輸資源的狀態表示,客戶端通過這些表示來了解資源狀態,并可能通過請求修改它。


總結式理解:

REST 的含義是:

通過標準的 HTTP 協議,客戶端和服務器傳輸資源的“表現形式”,并通過這些表現形式實現系統狀態的變更或查詢。

它的關鍵思想包括:

  • 使用統一接口(如 HTTP GET、POST、PUT、DELETE)
  • 無狀態通信(每個請求都不依賴前一個)
  • 資源通過 URL 唯一定位
  • 數據表現形式通常是 JSON、XML 等

  • 設計分布式系統的架構風格
    REST 是一種設計網絡分布式系統的架構模式,強調系統組件之間的松耦合和可伸縮性。

  • 一個服務如果滿足以下約束,則稱為 RESTful
    RESTful 服務必須遵循一組設計原則:

    • 客戶端-服務器分離
      客戶端和服務器職責分明,客戶端負責用戶界面,服務器負責數據存儲和處理。

    • 無狀態
      每個請求都獨立,服務器不會保存客戶端的會話狀態,簡化服務器設計并提升可伸縮性。

    • 統一接口
      通過統一的接口(如 HTTP 標準方法)與資源交互,降低系統復雜度。

    • 可緩存
      響應可以被緩存,提升性能和可伸縮性。

    • 分層系統
      系統可以由多個層組成,每層只與相鄰層通信,支持負載均衡和安全策略。

    • 按需代碼(可選)
      服務器可以將可執行代碼(如 JavaScript)傳送到客戶端擴展功能。

  • 對于 Web 服務來說,這意味著:

    • 通過 URI 與資源交互
      資源通過唯一的 URI(統一資源標識符)定位和訪問。

    • 使用 HTTP 方法作為接口
      典型方法如 GET(讀取)、POST(創建)、PUT(更新)、DELETE(刪除)等。

    • 無特定數據格式,但通常使用 JSON
      數據格式靈活,但 JSON 是最常見的傳輸格式,便于閱讀和解析。


有哪些實際的例子呢?

流行的網絡服務/網絡接口(Web APIs)

服務協議/標準

  • Google Maps:HTTP、URI、XML、JSON
  • Facebook Graph API:HTTP、URI、OAuth、JSON
  • Twitter:HTTP、URI、OAuth、JSON
  • Amazon S3:HTTP、URI、XML
  • Azure Storage:HTTP、URI、XML、JSON(表格)
  • Dropbox:HTTP、URI、OAuth、JSON
  • WordPress:HTTP、URI、OAuth、JSON

常見協議和標準包括 HTTP、URI、JSON、OAuth。
這只是其中一小部分網絡接口,實際上還有非常多,值得進一步了解和探索。

我現在怎么使用這些網絡接口(Web APIs)?

可用的選項

有些服務提供專門的客戶端 SDK,不過大多數可能不支持 C++。
你可以直接向公開的 HTTP 接口發請求,大多數語言(除 C++ 之外)都有很成熟的庫支持。
那么 C++ 怎么辦呢?可以使用現成的 HTTP 庫,或者在平臺支持的情況下用特定平臺的 HTTP API,也可以自己基于 TCP socket 實現 HTTP。
如果你還關注異步處理、跨平臺兼容性和符合現代 C++11 風格的代碼,這就更有挑戰,需要更合適的庫或框架來支持這些需求。

尤其是 C++ 的特殊性,調用 Web API 有多種方式,但 C++ 缺乏直接成熟的庫支持,特別是異步和跨平臺方面更難實現,需要開發者選擇合適的工具或自行實現相關功能。

The C++ Rest SDK

C++ Rest SDK 是一個專門為 C++ 設計的庫,幫助開發者更方便地訪問各種 Web 服務接口。它不追求從零開始,而是整合已有技術,提供統一且易用的接口,從而降低開發復雜度,提高效率。

  • 該 SDK 旨在彌補現有工具在 C++ 訪問網絡服務方面的不足,提供一套高層次的 API,涵蓋 HTTP、URI、JSON、OAuth 以及 WebSockets 等核心功能模塊,方便開發者直接調用。
  • 它不是從頭重寫所有東西,而是聰明地復用現有的開源庫和操作系統/平臺提供的 API,減少重復勞動。
  • 最終目標是讓開發者更簡單地調用 Web API,同時也方便他們基于此庫來編寫自己的客戶端 SDK。

整體來說,它是一個集成式的、高效的網絡服務訪問框架,專為現代 C++ 開發而設計。

  • 簡單的 API:比起功能繁雜,提供一個清晰、直接易用的接口更重要,降低使用門檻。
  • 異步支持:所有輸入輸出操作以及可能耗時的工作都采用異步處理,避免阻塞,提升效率和響應能力。
  • C++11 風格:采用現代 C++11 的語法和特性,讓代碼更簡潔、表達力更強。
  • 跨平臺兼容:演示的代碼可以運行在多種平臺上,包括 Windows 桌面和服務器(從 XP 開始)、Windows Store、Windows Phone、macOS(OS X)、iOS、Ubuntu 和 Android,體現了良好的跨平臺能力。

Asynchrony pplx::task

pplx::task

Parallel Patterns Library(PPL)提供了跨平臺的任務處理功能,特點是:

  • 它的任務(tasks)類似于 C++ 標準庫中的 std::future,但增加了“續作”(continuations)功能,也就是說,任務完成后可以自動觸發后續任務,方便實現異步操作的鏈式調用。

  • 將來這些 PPL 的任務模型可能會被更新的 futures 標準(如提案 N3970)所替代。

  • 目前有很多關于基于任務的編程的演講和資料,可以學習如何用這種方式來寫異步代碼。

總結就是:PPL 的任務機制為跨平臺異步編程提供了便利,比 std::future 更靈活,支持連續執行的異步操作。

pplx::task – continuations

pplx::task 是 C++ Parallel Patterns Library (PPL) 中用于異步編程的一個核心類,類似于 std::future,但更強大。

“continuations”(續作) 指的是你可以在一個異步任務完成后,鏈式地綁定后續操作,也就是說,當這個任務結束時,會自動觸發接下來的操作,而不需要手動等待。

舉個簡單例子:

pplx::task<int> t = some_async_operation();
t.then([](int result) {// 這個 lambda 是 continuation,等任務完成后執行std::cout << "結果是: " << result << std::endl;
});

這樣,then 里的代碼會在異步任務完成后自動運行,方便寫出清晰且非阻塞的異步代碼。

總結:

  • pplx::task 表示一個異步操作。
  • Continuations 是鏈式回調,任務完成后自動調用。
  • 讓異步流程更自然、可讀,避免“回調地獄”。
pplx::task<int> intTask = start_op();
intTask.then([](int value) {// Execute some work once operation has completed...
});

這段代碼展示了 pplx::task<int> 的基本用法和續作(continuation)的概念:

  • start_op() 是一個返回 pplx::task<int> 的異步函數,表示一個異步操作,最終會產生一個 int 類型的結果。
  • intTask 接收這個異步任務。
  • intTask.then(...) 注冊了一個續作(continuation),也就是當異步任務完成后自動執行的函數。
  • 傳入的 lambda 函數參數 value 就是異步操作完成后的結果(int 類型)。
  • 續作里的代碼會在 start_op() 異步完成后被調用,可以繼續執行后續邏輯。

簡單理解:

“先啟動一個異步操作,等它結束了自動調用后續函數,拿到結果后做處理。”

這就是基于 pplx::task 和續作機制的異步編程方式,方便鏈式調用和非阻塞操作。

Can also compose tasks using pplx::when_any and pplx::when_all constructs

你還可以使用 pplx::when_anypplx::when_all 這兩個構造函數來組合多個 pplx::task 任務。

  • pplx::when_any:當一組任務中任意一個完成時就觸發,返回最快完成的那個任務結果。
  • pplx::when_all:等待一組任務全部完成后觸發,返回所有任務的結果。

這兩個函數非常適合處理多個異步操作的同步和協調。

理解成:

你可以把多個異步任務綁在一起,要么等第一個完成(when_any),要么等全部完成(when_all),方便管理復雜異步流程。

pplx::task<int> intTask = pplx::create_task([]() -> int { throw std::runtime_error("error"); });
intTask.then([](pplx::task<int> op) {try {int value = op.get();Task based continuation, always executes} catch (const std::runtime_error &e) {
/* Perform error handling...  */ }
});

這段代碼的意思是:

  1. 創建一個任務 intTask,任務體是一個拋出 std::runtime_error 異常的函數。
    pplx::create_task 接受一個 lambda,返回一個 pplx::task<int> 對象。這個 lambda 執行時直接拋異常。

  2. 在這個任務完成后調用 .then(...) 注冊一個續續操作(continuation)。
    這個續續操作接受一個 pplx::task<int> 對象 op,它代表之前的任務。

  3. 續續操作里面用 try-catch 來處理任務執行結果。

    • 調用 op.get() 會嘗試獲取任務結果。
    • 如果任務成功完成,get() 返回結果值。
    • 如果任務執行時拋了異常(這里正好是 std::runtime_error),get() 會重新拋出該異常,于是進入 catch 塊,進行錯誤處理。
  4. 續續操作無論任務成功還是失敗都會執行,因為續續是綁定在任務完成后的回調。

總結:
這段代碼展示了如何用 pplx::task 異步執行一個可能拋異常的操作,并且在續續里優雅地捕獲并處理異常,保證異步任務鏈中錯誤不會未處理地傳遞。

如果用簡單語言說,就是:

異步操作執行過程中可能發生錯誤,我們用續續函數捕獲并處理錯誤,保證程序健壯。

http_client

  • 支持 HTTP/1.1 協議,基于客戶端發起的請求/響應模式。
  • 提供了簡單的高層 API,方便管理 HTTP 請求。
  • 不需要開發者自己管理底層的連接細節。

也就是說,這個庫幫你封裝了 HTTP 協議細節,使用時只需專注于發請求和收響應,無需操心連接的建立、維護和關閉等復雜工作。

http_client– hello world upload

http_client client(U("http://myserver.com"));
http_response response = client.request(methods::POST, U("mypath"), U("Hello World")).get();
if (response.status_code() == status_codes::OK) {// Inspect response...
}
``
* 創建一個 `http_client` 對象,目標服務器地址是 `http://myserver.com`。
* 通過這個客戶端,發送一個 POST 請求,請求路徑是 `mypath`,請求體內容是 `"Hello World"`。
* 使用 `.get()` 同步等待請求結果返回,得到 `http_response` 對象。
* 判斷響應的狀態碼是否是 200(OK)。
* 如果狀態碼是 OK,就可以繼續處理或查看響應內容。這里的 ‘U’ 是一個宏或函數,用來創建平臺相關的字符串字面量,保證代碼在不同系統上都能正確處理字符串編碼。* 在 Windows 平臺上,使用的是 UTF-16 編碼,字符串類型通常是 `std::wstring`。
* 在其他平臺(如 Linux、macOS)上,使用的是 UTF-8 編碼,字符串類型通常是 `std::string`。通過 ‘U’ 包裹字符串,代碼可以透明地適配不同平臺的字符串類型,避免編碼和類型不匹配的問題。```cpp
// Manually build up request.
http_request req(methods::POST);
req.set_request_uri(U("mypath"));
req.headers().add(header_names::user_agent, U(“myclient"));
req.set_body(U("Hello World"));
http_response response = client.request(req).get();

這段代碼展示了如何手動構建一個 HTTP POST 請求并發送:

  1. http_request req(methods::POST);
    創建一個 HTTP 請求對象,指定請求方法為 POST。

  2. req.set_request_uri(U("mypath"));
    設置請求的 URI 路徑為 "mypath"

  3. req.headers().add(header_names::user_agent, U("myclient"));
    在請求頭中添加 User-Agent 字段,值為 "myclient"

  4. req.set_body(U("Hello World"));
    設置請求體內容為 "Hello World"

  5. http_response response = client.request(req).get();
    使用之前定義的 client 發送該請求,并同步等待獲取響應。

總結:
這段代碼通過手動設置 URI、請求頭和請求體,展示了比直接調用 client.request(method, uri, body) 更靈活的請求構造方式。

http_client 還支持接收配置參數,用來設置一些選項,比如請求超時、數據塊大小(chunk size)等。

這讓你可以根據需要靈活調整客戶端行為,比如控制等待響應的最長時間,或者調整數據傳輸的緩沖大小,以優化性能或適應不同網絡環境。

http_client client(U("http://myserver.com"));
http_response response = client.request(methods::GET).get();
if (response.status_code() == status_codes::OK) {const utility::string_t body = response.extract_string().get();
}

這段代碼演示了使用 http_client 發起一個簡單的 GET 請求,并處理響應的過程:

  1. 創建一個 http_client 對象,目標地址是 "http://myserver.com"U 宏用來處理跨平臺字符串編碼。
  2. 使用 client.request(methods::GET).get() 發送一個同步的 GET 請求,等待服務器響應。
  3. 檢查響應的狀態碼是否為 OK(即 HTTP 200,表示請求成功)。
  4. 如果成功,調用 response.extract_string().get() 從響應中提取字符串內容(即服務器返回的正文),并保存到 body 變量中。

總結:這是一個標準的同步 HTTP GET 請求流程,拿到結果后轉成字符串以便后續使用。
http_response 對象提供了多種方法,可以將響應體(body)以不同形式提取出來,具體包括:

  • 作為字符串(string)
  • 作為 JSON 對象(方便處理結構化數據)
  • 作為字節向量(vector,適合二進制數據)
  • 作為數據流(stream,適合大文件或分塊讀取)

這使得處理 HTTP 響應更加靈活,可以根據需求選擇合適的數據形式。

http_client client(U("http://myserver.com"));
client.request(methods::GET).then([](http_response response) {// Check status code...return response.extract_string();}).then([](const utility::string_t &body) {// Use string...});

這段代碼示范了如何用異步方式通過 http_client 發起一個 GET 請求并處理響應:

  1. client.request(methods::GET)
    異步發起一個 GET 請求,返回一個 pplx::task<http_response>,表示未來某個時間會得到服務器的響應。

  2. .then([](http_response response) { ... })
    請求完成后,這個回調被調用,接收 http_response 對象。
    里面可以檢查狀態碼,確保請求成功,然后調用 response.extract_string(),異步提取響應體為字符串,返回一個新的任務。

  3. .then([](const utility::string_t &body) { ... })
    上一個任務完成后,這個回調被調用,拿到響應體字符串 body,可以對數據進行處理。

總結:
這是典型的基于任務鏈(continuations)的異步編程風格,通過連續調用 .then() 把異步操作串聯起來,代碼清晰且不阻塞線程。

http_client client(U("http://myserver.com"));
pplx::task<http_response> pendingRequest = client.request(methods::GET);
pendingRequest.then([](http_response response)  //  Entire request sent, response headers arrived{pplx::task<utility::string_t> extractString = response.extract_string();return extractString;}).then([](const utility::string_t &body)  // Entire response arrived{// Use string...});

這段代碼展示了如何用 http_client 發起異步 GET 請求,并分步驟處理響應:

  1. client.request(methods::GET);
    發起異步 GET 請求,返回一個 pplx::task<http_response>,表示未來會有響應。

  2. .then([](http_response response) {...})
    這是第一個回調,當請求完成且響應頭接收到時調用。
    里面調用 response.extract_string(),開始異步提取完整響應體(字符串形式),返回另一個任務 pplx::task<utility::string_t>

  3. return extractString;
    把這個任務返回,允許鏈式調用繼續等待響應體的讀取完成。

  4. .then([](const utility::string_t &body) {...})
    第二個回調,等響應體完全接收后執行,拿到響應體字符串,可以進行具體業務處理。

總結:
這個流程展示了異步處理 HTTP 響應的兩個階段:

  • 先收到響應頭,開始解析數據
  • 等完整數據到達后,再處理響應體

通過鏈式 .then(),代碼清晰且非阻塞。

光說不練確實不容易信服

It’s not that I don’t believe you, but I need to see it

http_client– efficient streaming

http_client 的高效流式處理,指的是它支持以流的方式處理 HTTP 響應體數據,而不是一次性把整個響應讀入內存。這種方式適合處理大文件下載、視頻流、或者任何大數據量的傳輸,避免內存占用過高。

具體特點包括:

  • 邊接收邊處理:響應體數據可以邊接收邊消費,無需等待整個響應完成。
  • 支持流接口:可以將響應體作為輸入流來讀取,比如 std::istream,方便與標準流操作結合。
  • 降低延遲和內存壓力:適合處理大文件或長連接,提升性能和資源利用率。

簡言之,http_client 通過流式接口,實現了高效、低開銷的 HTTP 數據傳輸,適合需要處理大數據或持續數據流的應用場景。

http_client client(U("http://myserver.com"));
// Create stream backed by a vector.
http_request request(methods::GET);
container_buffer<std::vector<uint8_t>> buffer;
buffer.collection().reserve(1024 * 1024 * 4);  // Heap allocation all at once up front
request.set_response_stream(buffer.create_ostream());
// Send request and wait for response to arrive.
http_response response = client.request(request).get();
response.content_ready().wait();
std::vector<uint8_t> body = std::move(buffer.collection());  // Important to move!

這段代碼演示了如何用 http_client 進行高效的流式接收,把 HTTP 響應體直接寫入一個預分配好的內存緩沖區(std::vector<uint8_t>),具體理解如下:

  1. http_client client(U("http://myserver.com"));
    創建一個 HTTP 客戶端,目標服務器地址是 "http://myserver.com"

  2. http_request request(methods::GET);
    構造一個 GET 請求。

  3. container_buffer<std::vector<uint8_t>> buffer;
    創建一個基于 std::vector<uint8_t> 的緩沖區,作為響應數據的流式存儲區。

  4. buffer.collection().reserve(1024 * 1024 * 4);
    一次性預先分配約 4MB 內存,避免多次重新分配,提高效率。

  5. request.set_response_stream(buffer.create_ostream());
    將請求的響應流綁定到這個緩沖區的輸出流,響應數據會寫入這里。

  6. http_response response = client.request(request).get();
    發送請求,阻塞等待響應。

  7. response.content_ready().wait();
    等待響應內容完全準備好。

  8. std::vector<uint8_t> body = std::move(buffer.collection());
    將緩沖區中的數據移動到 body,避免額外拷貝,提升性能。

總結
這段代碼展示了如何用流式方式將 HTTP 響應體數據直接寫入內存緩沖區,適合大數據量接收,避免一次性拷貝或內存浪費,是高效網絡數據處理的典型寫法。

// Buffer backed by a file.
streambuf<uint8_t> buffer = file_buffer<uint8_t>::open(U("myfile")).get();
// Buffer backed by raw memory.
const size_t size = 1024 * 1024 * 4;
char *raw = new char[size];
rawptr_buffer<uint8_t> buffer(reinterpret_cast<uint8_t *>(raw), size);

這段代碼展示了兩種用不同類型緩沖區支持流的方式,具體理解如下:

  1. 文件緩沖區
streambuf<uint8_t> buffer = file_buffer<uint8_t>::open(U("myfile")).get();
  • 通過 file_buffer<uint8_t>::open(U("myfile")) 打開一個名為 "myfile" 的文件,并返回一個異步任務。
  • .get() 等待任務完成并獲得與文件關聯的緩沖區對象。
  • 這個緩沖區將會直接操作文件,適合流式讀寫大文件,避免一次性全部加載到內存。
  1. 原始內存緩沖區
const size_t size = 1024 * 1024 * 4;
char *raw = new char[size];
rawptr_buffer<uint8_t> buffer(reinterpret_cast<uint8_t *>(raw), size);
  • 手動在堆上分配一塊 4MB 的原始內存(char* raw)。
  • 使用 rawptr_buffer<uint8_t> 把這塊原始內存封裝成緩沖區,傳給流式接口使用。
  • 這樣可以用自定義內存管理或與已有內存結合,適合不想復制數據,直接操作內存的場景。

總結
這兩種緩沖區類型(文件緩沖區和原始內存緩沖區)為流式數據處理提供了靈活的底層支持,可以針對不同應用場景優化數據讀寫方式。

這段內容講的是使用“連續存儲緩沖區”(buffer backed with contiguous storage)時帶來的性能優化和具體應用,理解如下:

  • 連續存儲緩沖區
    使用內存連續的一塊區域作為緩沖區,比如 std::vector 或原始內存塊,可以讓某些平臺上的操作更高效。因為連續內存便于快速訪問和處理。

  • 調整消息體的塊大小(chunk size)
    合理設置數據塊的大小,避免頻繁地上下棧傳遞數據,從而減少開銷,提高性能。

  • 獲取指向內部存儲的指針
    可以直接拿到緩沖區內部連續存儲的指針,方便和底層平臺接口交互,減少復制。

  • 發送請求體時
    直接把緩沖區指針傳遞給底層平臺 API 進行讀取,避免不必要的數據復制或轉換。

  • 接收響應體時
    直接把數據寫入緩沖區對應的內存區域,也就是傳給平臺 API 直接寫入緩沖區。

總結就是:通過使用連續內存的緩沖區,并且配合底層平臺接口的直接讀寫,可以顯著提升網絡請求/響應的處理效率,減少內存操作開銷。

websocket_client

這段內容講的是WebSockets技術及其特點,理解如下:

  • 支持雙向通信
    WebSocket允許在建立一次持久的TCP連接后,客戶端和服務器之間可以雙向實時通信,不再局限于傳統的HTTP請求-響應模式。

  • 與HTTP請求/響應模型對比
    傳統HTTP是客戶端發請求,服務器回應;而WebSocket連接建立后,服務器可以主動推送消息給客戶端,不需要客戶端每次都發請求。

  • 應用場景
    WebSocket適合頻繁的小消息交換場景,比如在線游戲、互動協作應用、聊天系統等,需要實時、低延遲的雙向通訊。

總結:WebSocket是建立在HTTP基礎上的一種協議,解決了傳統HTTP通信單向性和請求響應模型的限制,使得客戶端和服務器可以持續且雙向地交換數據,非常適合實時交互類應用。

websocket_client client;
client.connect(U("ws://myserver.com")).then([] {// Connection successfully opened...
});
// Later on when done...
client.close().then([] {// Connection is closed...
});

這段代碼演示了如何用 C++ Rest SDK 使用 websocket_client 來建立和關閉 WebSocket 連接,理解如下:

  • websocket_client client;
    創建一個 WebSocket 客戶端實例。

  • client.connect(U("ws://myserver.com"))
    向指定的 WebSocket 服務器發起連接請求,U() 是平臺相關的字符串封裝。

  • .then([] { ... });
    連接成功后執行的回調函數,表示連接已經打開。

  • client.close()
    關閉當前的 WebSocket 連接。

  • .then([] { ... });
    連接關閉后執行的回調函數,表示連接已經關閉。

總結:這段代碼展示了WebSocket連接的建立和關閉過程,且都是異步操作,配合回調來處理連接成功和關閉事件。

websocket_client 也支持配置選項,比如:

  • HTTP 升級請求頭(Upgrade request headers)
    在建立 WebSocket 連接的過程中,客戶端需要通過 HTTP 請求發起“協議升級”(Upgrade)請求,websocket_client 允許你自定義這些請求頭,比如添加身份驗證信息、自定義頭字段等。

  • 子協議(Subprotocols)
    WebSocket 支持協商“子協議”,用于定義客戶端和服務器之間通信的具體協議(比如使用 JSON-RPC、chat 協議等)。websocket_client 可以配置要使用的子協議,讓服務器知道你打算用哪種通信方式。

總結

你可以通過配置 websocket_client 來更精細地控制連接行為,例如添加認證信息或聲明使用的通信協議,使其更好地適應復雜的網絡場景。

websocket_client– sending

// Strings
websocket_outgoing_message msg;
msg.set_utf8_message("Hello World");
pplx::task<void> sendTask = client.send(msg);
// Binary
websocket_outgoing_message msg;
streambuf<uint8_t> buffer = file_buffer<uint8_t>::open(U("myfile")).get();
msg.set_binary_message(buffer.create_istream(), 10);  // Send only 10 bytes
pplx::task<void> sendTask = client.send(msg);

這段代碼展示了如何使用 websocket_client 通過 WebSocket 發送消息,包含 兩種類型的消息:字符串(文本)和二進制數據。


文本消息發送(UTF-8 編碼字符串)

websocket_outgoing_message msg;
msg.set_utf8_message("Hello World");
pplx::task<void> sendTask = client.send(msg);
理解:
  1. 創建一個 websocket_outgoing_message 對象。
  2. 使用 set_utf8_message() 設置要發送的文本內容。
  3. 調用 client.send(msg) 異步發送這條消息。

二進制消息發送(從文件讀取數據)

websocket_outgoing_message msg;
streambuf<uint8_t> buffer = file_buffer<uint8_t>::open(U("myfile")).get();
msg.set_binary_message(buffer.create_istream(), 10);  // Send only 10 bytes
pplx::task<void> sendTask = client.send(msg);
理解:
  1. 創建一個 websocket_outgoing_message 對象。
  2. 使用 file_buffer 打開名為 "myfile" 的文件,并創建一個輸入流。
  3. 使用 set_binary_message() 設置要發送的二進制數據,最多發送 10 個字節。
  4. 通過 client.send(msg) 異步發送消息。

核心理解:

  • websocket_outgoing_message 是 WebSocket 客戶端用于封裝消息的對象。

  • 可以發送:

    • UTF-8 文本消息(通常用于聊天、命令等)
    • 二進制消息(例如圖像、音頻、文件片段)
  • 所有發送都是 異步的,通過 pplx::task 表示操作結果,可繼續添加 .then(...) 處理發送成功或失敗。

websocket_client– receiving

websocket_client client;
client.receive().then([](websocket_incoming_message msg) {return msg.extract_string();  // Msg body could still be arriving}).then([](std::string &data) {});

這段代碼展示了如何使用 websocket_client 異步接收來自服務器的 WebSocket 消息,并提取為字符串進行處理。我們來逐步理解:


代碼解析:

websocket_client client;client.receive().then([](websocket_incoming_message msg) {return msg.extract_string();  // 消息內容可能還在到達過程中}).then([](std::string &data) {// 在這里處理消息內容});

逐步理解:

  1. client.receive()

    • 啟動異步接收操作。
    • 一旦有來自服務器的消息,這個操作就會完成,并傳入 websocket_incoming_message 對象。
  2. .then([](websocket_incoming_message msg) { return msg.extract_string(); })

    • 提取消息體(內容)為字符串。
    • 注意注釋:消息體可能還沒有完全到達,所以 extract_string() 返回的是一個異步任務(pplx::task<std::string>)。
    • 它會等待直到消息全部到達,然后提取為 std::string
  3. .then([](std::string &data) { ... })

    • 當字符串準備好之后,調用這個函數處理提取出來的消息內容。

總結:

  • 這是典型的異步編程模式(使用 pplx::task)。
  • receive() -> 接收到消息對象;
  • extract_string() -> 提取消息體為字符串(可能需要等待);
  • 第二個 .then 是真正的業務處理邏輯,比如打印消息、邏輯判斷等。
  • 支持 無阻塞消息處理,很適合 WebSocket 這種持續通信的場景。

類比:

就像你在等快遞:

  • 快遞車(WebSocket)來了,但你還要打開箱子提取內容(extract_string)。
  • 然后你才真正使用里面的物品(處理 data)。

WebSocket 接收消息的兩種更高級的處理方式。我們來逐條理解:


1. 也可以將消息體作為流來訪問

  • 除了提取為字符串(extract_string()),還可以用 流(stream) 的方式讀取消息體。
  • 這在處理大消息二進制數據時特別有用,因為你可以一邊接收、一邊處理,避免一次性加載全部內容到內存中。

類比:就像你可以選擇一次性讀完一本書(字符串),也可以一頁一頁地讀(流)。


2. 添加了通過回調函數重復接收消息的選項

  • WebSocket 是持久連接,服務器可以持續推送消息。

  • 可以設置一個回調函數,每當有新消息到達時就自動調用,無需每次都手動調用 .receive()

  • 這使得程序更自動、更高效地持續處理接收的數據,非常適合:

    • 聊天應用
    • 實時監控系統
    • 多人在線協作

總結

這些特性使 WebSocket 客戶端:

  • 更靈活(字符串 vs 流)
  • 更智能(手動輪詢 vs 自動回調)
  • 更適合持續、實時、高頻率通信的應用場景。

Cross platform learning

這段內容講的是構建跨平臺 C++ 庫(如 C++ REST SDK)時的測試和平臺兼容性策略,逐條解釋如下:


自動化測試基礎設施

  • Gated、Rolling、Nightly

    • Gated:每次代碼合并前先跑測試,確保不會破壞主分支。
    • Rolling:持續集成方式,代碼變更后自動觸發構建和測試。
    • Nightly:每天自動構建并運行所有測試,檢測潛在問題。

這些機制保障了代碼的穩定性和高質量


默認將測試用例設計為跨平臺

  • 不只是代碼要能跨平臺運行,測試也要能在各個平臺(Windows、Linux、macOS 等)上跑。
  • 有助于盡早發現平臺差異導致的問題。

小心構建系統的復雜性

  • 支持多個平臺可能導致 構建腳本、依賴管理、工具鏈配置變得復雜
  • 要注意保持構建系統的清晰和可維護性(例如使用 CMake 時注意條件判斷不要過多嵌套)。

把“平臺”當作“功能”來看待

  • 不同平臺本身就可以看作是“功能特性”:

    • 支持 Windows 是一個功能;
    • 支持 Android 是另一個功能;
    • 不同平臺的功能支持情況也可能不同(比如是否支持 WebSocket)。

有時需要寫小的 shim(適配層)來屏蔽缺失庫

  • 有的平臺沒有某些標準庫或第三方庫的支持。
  • 需要寫小工具或封裝層(shim)來“填補空缺”,統一對外 API。

即使用戶當前不需要跨平臺,也希望你“提供選項”

  • 用戶喜歡庫具備跨平臺潛力,即便他們現在只在某個平臺用。
  • 建議:將 API 層次分明設計,允許用戶下沉到底層平臺 API,以滿足高級需求。

總結

這部分強調:

  • 自動化測試的重要性(尤其在跨平臺項目中);
  • 構建系統與平臺兼容性要簡單、靈活、可維護
  • 設計庫時給用戶留下自由度和擴展性,這能提高庫的實用價值和用戶滿意度。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/85113.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/85113.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/85113.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【OpenHarmony】【交叉編譯】使用gn在Linux編譯3568a上運行的可執行程序

linux下編譯arm64可執行程序 一.gn ninja安裝二.交叉編譯工具鏈安裝1.arm交叉編譯工具2.安裝arm64編譯器 三. gn文件添加arm及arm64工具鏈四.編譯驗證 本文以gn nijia安裝中demo為例&#xff0c;將其編譯為在arm64(rk_3568_a開發板)環境下可運行的程序 一.gn ninja安裝 安裝g…

【開發心得】AstrBot對接飛書失敗的問題探究

飛書與AstrBot的集成使用中,偶爾出現連接不穩定的現象。盡管不影響核心功能,但為深入探究技術細節并推動后續優化,需系統性記錄該問題。先從底層通信機制入手,分析連接建立的邏輯與數據交互流程。基于實際現象,明確問題發生的具體場景和表現特征,進而梳理潛在影響因素,為…

Spring Boot 3.5.0中文文檔上線

Spring Boot 3.5.0 中文文檔翻譯完成&#xff0c;需要的可收藏 傳送門&#xff1a;Spring Boot 3.5.0 中文文檔

7.atlas安裝

1.服務器規劃 軟件版本參考&#xff1a; https://cloud.google.com/dataproc/docs/concepts/versioning/dataproc-release-2.2?hlzh-cn 由于hive3.1.3不完全支持jdk8,所以將hive的版本調整成4.0.1。這個版本沒有驗證過&#xff0c;需要讀者自己抉擇。 所有的軟件都安裝再/op…

c# 獲取電腦 分辨率 及 DPI 設置

using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices;/// <summary> /// 這個可以 /// </summary> class Program {static void Main(){//設置DPI感知try{SetProcessDpiAwareness(…

LangChain表達式(LCEL)實操案例1

案例1&#xff1a;寫一篇短文&#xff0c;然后對這篇短文進行打分 from langchain_core.output_parsers import StrOutputParser from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder from langchain_core.runnables import RunnableWithMessageHist…

OleDbParameter.Value 與 DataTable.Rows.Item.Value 的性能對比

OleDbParameter.Value 與 DataTable.Rows.Item.Value 的性能對比 您提到的兩種賦值操作屬于不同場景&#xff0c;它們的性能和穩定性取決于具體使用方式。下面從幾個維度進行分析&#xff1a; 1. 操作本質對比 &#xff08;1&#xff09;OleDbParameter.Value 用途&#xf…

【Opencv+Yolo】Day2_圖像處理

目錄 一、圖像梯度計算 圖像梯度-sobal算子&#xff1a; Scharr&#xff1a;權重變化更大&#xff08;線條更加豐富&#xff0c;比Sobel更加細致捕捉更多梯度信息&#xff09; Laplacian算子&#xff1a;對噪音點敏感&#xff08;可以和其他一起結合使用&#xff09; 二、邊…

STM32通過rt_hw_hard_fault_exception中的LR寄存器追溯程序問題?

1. 問題現象 程序運行導致rt_hw_hard_fault_exception 如圖 顯示錯誤相關代碼 struct exception_stack_frame {uint32_t r0;uint32_t r1;uint32_t r2;uint32_t r3;uint32_t r12; uint32_t lr; // 鏈接寄存器 (LR)uint32_t pc; // 程序計數器 (PC)uint32_t psr; // 程序狀態…

Mac安裝配置InfluxDB,InfluxDB快速入門,Java集成InfluxDB

1. 與MySQL的比較 InfluxDBMySQL解釋BucketDatabase數據庫MeasurementTable表TagIndexed Column索引列FieldColumn普通列PointRow每行數據 2. 安裝FluxDB brew update默認安裝 2.x的版本 brew install influxdb查看influxdb版本 influxd version # InfluxDB 2.7.11 (git: …

【spring】spring中的retry重試機制; resilience4j熔斷限流教程;springboot整合retry+resilience4j教程

在調用三方接口時&#xff0c;我們一般要考慮接口調用失敗的處理&#xff0c;可以通過spring提供的retry來實現&#xff1b;如果重試幾次都失敗了&#xff0c;可能就要考慮降級補償了&#xff1b; 有時我們也可能要考慮熔斷&#xff0c;在微服務中可能會使用sentinel來做熔斷&a…

(21)量子計算對密碼學的影響

文章目錄 2??1?? 量子計算對密碼學的影響 &#x1f30c;&#x1f50d; TL;DR&#x1f680; 量子計算&#xff1a;密碼學的終結者&#xff1f;? 量子計算的破壞力 &#x1f510; Java密碼學體系面臨的量子威脅&#x1f525; 受影響最嚴重的Java安全組件 &#x1f6e1;? 后…

經營分析會,財務該怎么做?

目錄 一、業績洞察&#xff1a;從「現象描述」到「因果分析」 1.分層拆解 2.關聯驗證 3.根因追溯 二、預算管理&#xff1a;從「剛性控制」到「動態平衡」 1.分類管控 2.滾動校準 3.價值評估 三、客戶與市場&#xff1a;從「交易記錄」到「價值評估」 1.價值分層 2.…

進階智能體實戰九、圖文需求分析助手(ChatGpt多模態版)(幫你生成 模塊劃分+頁面+表設計、狀態機、工作流、ER模型)

?? 基于 ChatGPT 多模態大模型的需求文檔分析助手 本文將介紹如何利用 OpenAI 的 GPT-4o 多模態能力,構建一個智能的需求文檔分析助手,自動提取功能模塊、菜單設計、字段設計、狀態機、流程圖和 ER 模型等關鍵內容。 一、?? 環境準備 在開始之前,請確保您已經完成了基礎…

圖書管理系統的設計與實現

湖南軟件職業技術大學 本科畢業設計(論文) 設計(論文)題目 圖書管理系統的設計與實現 學生姓名 學生學號 所在學院 專業班級 畢業設計(論文)真實性承諾及聲明 學生對畢業設計(論文)真實性承諾 本人鄭重聲明:所提交的畢業設計(論文)作品是本人在指導教師的指導下,獨…

直線模組在手術機器人中有哪些技術挑戰?

手術機器人在現代醫療領域發揮著越來越重要的作用&#xff0c;直線模組作為其關鍵部件&#xff0c;對手術機器人的性能有著至關重要的影響。然而&#xff0c;在手術機器人中使用直線模組面臨著諸多技術挑戰&#xff0c;具體如下&#xff1a; 1、?高精度要求?&#xff1a;手術…

技術-工程-管用養修保-智能硬件-智能軟件五維黃金序位模型

融智學工程技術體系&#xff1a;五維協同架構 基于鄒曉輝教授的框架&#xff0c;工程技術體系重構為&#xff1a;技術-工程-管用養修保-智能硬件-智能軟件五維黃金序位模型&#xff1a; math \mathbb{E}_{\text{技}} \underbrace{\prod_{\text{Dis}} \text{TechnoCore}}_{\…

InnoDB引擎邏輯存儲結構及架構

簡化理解版 想象 InnoDB 是一個高效運轉的倉庫&#xff1a; 核心內存區 (大腦 & 高速緩存 - 干活超快的地方) 緩沖池 Buffer Pool (最最核心&#xff01;)&#xff1a; 作用&#xff1a; 相當于倉庫的“高頻貨架”。把最常用的數據&#xff08;表數據、索引&#xff09;從…

貧血模型與充血模型:架構設計的分水嶺

在企業級應用的架構設計中&#xff0c;貧血模型和充血模型一直是架構師們爭論的熱點話題。兩者背后分別代表著“事務腳本模式”和“領域模型模式”兩種截然不同的設計思想。而理解這兩者的差異&#xff0c;有助于開發者根據實際業務場景做出更合理的架構決策。 貧血模型&#…

Linux的調試器--gbd/cgbd

1.引入 #include <stdio.h> int Sum(int s, int e) {int result 0;for(int i s; i < e; i){result i;}return result; } int main() {int start 1;int end 100;printf("I will begin\n");int n Sum(start, end);printf("running done, result i…