Asio 的 buffer
是什么?
boost::asio::buffer(...)
是一個函數模板,用于創建一個通用的 buffer
對象,可傳遞給 I/O 函數(如 read
, write
, read_some
, write_some
等)。
它返回的是 mutable_buffer
或 const_buffer
的對象,取決于你傳入的參數是可寫還是只讀。
boost::asio::buffer(char* data, std::size_t size); // 返回 mutable_buffer
boost::asio::buffer(const char* data, std::size_t size); // 返回 const_buffer
boost::asio::buffer(std::string&) // mutable_buffer
boost::asio::buffer(const std::string&) // const_buffer
boost::asio::buffer(std::vector<char>&) // mutable_buffer
mutable_buffer
與 const_buffer
的區別
類型 | 權限 | 用途 | 舉例場景 |
---|---|---|---|
mutable_buffer | 可寫 | 讀取數據(recv) | read_some() 、async_read() |
const_buffer | 只讀 | 寫入數據(send) | write_some() 、async_write() |
-
mutable_buffer
只能傳給讀操作,如socket.read_some()
; -
const_buffer
只能傳給寫操作,如socket.write_some()
;
buffer 支持多塊
可以將多個 buffer 組成一個 buffer sequence:
char part1[64], part2[64];
std::vector<boost::asio::mutable_buffer> bufs {boost::asio::buffer(part1),boost::asio::buffer(part2)
};
socket.read_some(bufs);
?buffer()
函數在調用時傳不傳 size
參數
char data[100];// ? 自動推導 size(推薦,謹慎)
auto buf1 = boost::asio::buffer(data); // size = 100// ? 手動指定 size(安全、精確)
auto buf2 = boost::asio::buffer(data, 64); // size = 64
示例 1:發送 buffer 超范圍
char data[100] = "hello";
auto buf1 = boost::asio::buffer(data); // size = 100
auto buf2 = boost::asio::buffer(data, 5); // size = 5socket.send(buf1); // ?? 發送了 100 字節,包含未初始化部分!
socket.send(buf2); // ? 只發送了 "hello"
示例 2:std::string 的問題
std::string str = "hello";
auto buf1 = boost::asio::buffer(str); // size = 5
auto buf2 = boost::asio::buffer(str, str.size()); // 等價于 buf1
auto buf3 = boost::asio::buffer(str.c_str()); // size = strlen(c_str()) + 1(包含 \0)??
情況 | 是否建議手動指定 size |
---|---|
發送部分數據 | ? 推薦 |
結構體轉 buffer 時 | ? 避免讀取未初始化區域 |
動態分配內存 | ? 明確 buffer 長度 |
string、vector 直接用 | ? 可不指定,自動使用 .size() |
char* /void* 原始指針 | ? 必須指定 size ! |
數據來源 | buffer 用法 | 推薦程度 |
---|---|---|
std::string | buffer(str) | ? 安全 |
std::vector | buffer(vec) | ? 安全 |
char[100] | buffer(arr) | ? 自動推導 |
char* ptr | buffer(ptr, length) | ? 必須指定 |
發送部分數據 | buffer(data, actual_data_size) | ? 強烈推薦 |
發送數據
(以下給出的函數原型與源碼不完全相同,只是作為大致用法的例子)
send
-
屬于
socket
的 成員函數; -
調用底層
::send()
系統調用; -
一次嘗試寫部分數據(可能寫不完);
-
你要自己處理剩余數據;
// 拋異常版本
std::size_t send(const boost::asio::const_buffer& buffer);// 可加 flags(如 MSG_DONTWAIT)
std::size_t send(const boost::asio::const_buffer& buffer, socket_base::message_flags flags);std::size_t send(const boost::asio::const_buffer& buffer, socket_base::message_flags flags, boost::system::error_code& ec);
write_some
-
同樣是 成員函數;
-
一次性嘗試寫 buffer 中盡量多的數據;
-
不保證寫滿;
-
適合你 控制寫的粒度,或用于非阻塞模式;
// 拋異常版本
std::size_t write_some(const boost::asio::const_buffer& buffer);// 使用 error_code
std::size_t write_some(const boost::asio::const_buffer& buffer, boost::system::error_code& ec);
write
-
是 全局函數模板;
-
會自動循環調用
write_some()
,直到寫完整個 buffer; -
適合你想 一次性寫完所有數據 的情況;
位于 boost/asio/write.hpp
,可用于任意 SyncWriteStream
類型(如 TCP socket):
// 單個 buffer,拋異常版本
template <typename SyncWriteStream, typename ConstBufferSequence>
std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers);// 單個 buffer,帶 error_code
template <typename SyncWriteStream, typename ConstBufferSequence>
std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers, boost::system::error_code& ec);// 可指定 completion condition(寫多少算完)
template <typename SyncWriteStream, typename ConstBufferSequence, typename CompletionCondition>
std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers, CompletionCondition completion_condition);template <typename SyncWriteStream, typename ConstBufferSequence, typename CompletionCondition>
std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers, CompletionCondition completion_condition, boost::system::error_code& ec);
特性 | send() | write_some() | write() |
---|---|---|---|
類型 | socket 的成員函數 | socket 的成員函數 | 全局模板函數 |
發送完整數據? | ? 只發送部分(不保證) | ? 不保證 | ? 會完整發送 |
底層系統調用 | 直接 send() | 直接 send() | 調用 write_some() 多次 |
適合場景 | 手動控制寫入過程 | 高性能或非阻塞寫場景 | 簡潔、完整性保證(推薦) |
使用復雜度 | 中 | 高 | ? 最簡單 |
支持 buffer sequence? | ? 單一 buffer | ? 單一 buffer | ? 可寫多 buffer(scatter I/O) |
支持 flags(如 MSG_NOSIGNAL) | ? 可以傳 flags 參數(低層控制) | ? 不支持 flags |
send()
是通用 socket 接口,適用于 TCP 和 UDP,底層等價于 BSD 的 send()
系統調用;而 write_some()
是流式 socket 的專用寫函數,更貼近 TCP 的流語義,不適用于 UDP。?
返回值
這三個函數的返回值都是 std::size_t
類型,表示“實際寫入/發送成功的字節數”,它們不會包含錯誤碼?
情況 | 是否可能返回 0? | 說明 |
---|---|---|
send() | ? 是 | Socket 被關閉,或對方關閉連接,或 buffer.size() == 0 |
write_some() | ? 是 | 同上 |
write() | ? 不會 | 內部循環,直到寫完或報錯為止,返回至少 1 或拋異常 |
Boost.Asio 中 send()
、write_some()
、write()
這三個函數的返回值永遠不會是負數。?
在傳統 POSIX socket 中,send()
、write()
返回的是 ssize_t
:?
-
如果出錯,返回 -1,并通過
errno
指出錯誤原因; -
這是 C 語言 API 的慣例。
Boost.Asio 的做法不同:
錯誤處理方式 | 返回負數? | 錯誤表現方式 |
---|---|---|
拋異常版本 | ? 否 | 拋出 boost::system::system_error 異常 |
error_code 版本 | ? 否 | 返回值為 0,錯誤通過 ec 指出 |
?接收數據
receive
這是底層 socket 的成員函數,可以接收部分數據,可用于 TCP 和 UDP。
// 拋異常版本
std::size_t receive(boost::asio::mutable_buffer buffer);// 帶 flags
std::size_t receive(boost::asio::mutable_buffer buffer, socket_base::message_flags flags);std::size_t receive(boost::asio::mutable_buffer buffer, socket_base::message_flags flags, boost::system::error_code& ec);
?read_some
這是 socket
的成員函數,僅用于 TCP(流式協議),功能類似于 recv()
,但偏高層。
// 拋異常版本
std::size_t read_some(boost::asio::mutable_buffer buffer);// 使用 error_code
std::size_t read_some(boost::asio::mutable_buffer buffer, boost::system::error_code& ec);
read
這是 Boost.Asio 的全局函數模板,會自動循環調用 read_some()
,直到讀完你想要的所有字節,適合完整讀取。
// 單個 buffer,拋異常版本
template <typename SyncReadStream, typename MutableBufferSequence>
std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers);// 帶 error_code
template <typename SyncReadStream, typename MutableBufferSequence>
std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers, boost::system::error_code& ec);// 可指定完成條件
template <typename SyncReadStream, typename MutableBufferSequence, typename CompletionCondition>
std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers, CompletionCondition completion_condition);template <typename SyncReadStream, typename MutableBufferSequence, typename CompletionCondition>
std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers, CompletionCondition completion_condition, boost::system::error_code& ec);
特性 | receive() | read_some() | read() |
---|---|---|---|
類型 | socket 的成員函數 | stream socket 的成員函數 | 全局模板函數 |
接收完整數據? | ? 只讀一部分 | ? 不保證 | ? 自動循環直到讀夠為止 |
底層系統調用 | 直接調用 recv() | 直接調用 recv() | 內部多次調用 read_some() |
支持協議 | ? TCP + UDP | ? TCP(流) | ? TCP(SyncReadStream 概念) |
支持 flags(如 MSG_PEEK ) | ? 支持 | ? 不支持 | ? 不支持 |
適合場景 | 精細控制、非阻塞收包 | 高性能讀入流數據 | ? 最推薦完整讀取某個對象/幀 |
使用復雜度 | 中 | 高 | ? 最簡單 |
支持 buffer sequence? | ? 不支持 | ? 不支持 | ? 支持 scatter buffer |
返回值?
函數名 | 返回值類型 | 返回值含義 |
---|---|---|
receive() | std::size_t | 成功讀取的字節數(可能是部分) |
read_some() | std::size_t | 本次調用成功讀取的字節數 |
read() | std::size_t | 多次調用累計讀取的總字節數(直到完成條件) |
函數 | 是否可能返回 0 | 說明 |
---|---|---|
receive() | ? 是 | socket 被關閉、對方斷開、buffer size 為 0 |
read_some() | ? 是 | 同上 |
read() | ? 否 | 會循環直到讀夠或拋異常,除非你指定了 completion 條件 |
?這三個函數?判斷對端關閉連接,要依靠 error_code
,而不是返回值!
-
可能返回 0:但返回 0 不一定意味著對方斷開
-
真正的連接關閉,需要檢查
error_code
判斷對方關閉連接的唯一正確方式是:ec == boost::asio::error::eof
。不要依賴返回值為 0。
正確的寫法(判斷連接關閉):
boost::system::error_code ec;
std::array<char, 1024> buf{};std::size_t len = socket.read_some(boost::asio::buffer(buf), ec);if (ec == boost::asio::error::eof) {std::cout << "對端已關閉連接(eof)" << std::endl;
} else if (ec) {std::cout << "其他錯誤: " << ec.message() << std::endl;
} else {std::cout << "收到數據: " << std::string(buf.data(), len) << std::endl;
}
成對用法
?send-receive
發送
// 準備要發送的數據
std::string msg = "hello"; // 不包含 \0,Asio 不自動加
msg.push_back('\0'); // 明確加上 \0(等價于你的 send_len = strlen+1)// 發送數據
boost::system::error_code ec;
std::size_t len = socket.send(asio::buffer(msg),0,ec);
?接收
// 接收數據
std::array<char, 100> recv_buf{};
boost::system::error_code ec;
std::size_t len = socket.receive(asio::buffer(recv_buf),0,ec);
write_some-read_some
發送
// 準備要發送的數據
std::string msg = "hello"; // 不包含 \0,Asio 不自動加
msg.push_back('\0'); // 明確加上 \0(等價于你的 send_len = strlen+1)// 發送數據
boost::system::error_code ec;
std::size_t len = socket.write_some(asio::buffer(msg),ec);
接收
// 接收數據
std::array<char, 100> recv_buf{};
boost::system::error_code ec;
std::size_t len = socket.read_some(asio::buffer(recv_buf),ec);
write-read
發送
// 準備要發送的數據
std::string msg = "hello"; // 不包含 \0,Asio 不自動加
msg.push_back('\0'); // 明確加上 \0(等價于你的 send_len = strlen+1)// 發送數據
boost::system::error_code ec;
std::size_t len = asio::write(socket, asio::buffer(msg), ec);
接收
// 接收數據
std::array<char, 100> recv_buf{};
boost::system::error_code ec;
//這里的buffer如果不指定,默認是100,但是我們沒發這么多
//如果不指定,它會一直等,但是我們另一端發完就把socket釋放了,于是這一端會出錯
std::size_t len = asio::read(socket, asio::buffer(recv_buf,6), ec);
阻塞與非阻塞
在 默認情況下(即你不手動設置非阻塞、也沒用異步函數):
Boost.Asio 中的 三類收發函數——
send()
/write_some()
/write()
和recv()
/read_some()
/read()
——都是阻塞的!
函數 | 默認是否阻塞? | 阻塞直到... |
---|---|---|
socket.send() | ? 是 | 至少部分數據寫入內核緩沖區或報錯 |
socket.write_some() | ? 是 | 寫入一部分成功或出錯 |
asio::write() | ? 是 | 全部 buffer 寫完或出錯(自動循環 write_some) |
函數 | 默認是否阻塞? | 阻塞直到... |
---|---|---|
socket.recv() | ? 是 | 至少讀入一部分或出錯 |
socket.read_some() | ? 是 | 一次性讀取當前內核中已有的數據或出錯 |
asio::read() | ? 是 | buffer 全部讀滿或出錯(自動循環 read_some) |
非阻塞 = 函數立即返回,不等待系統緩沖區可用或數據可達
-
如果不能立即完成,函數:
-
拋出
boost::asio::error::would_block
-
或返回 0 并設置
error_code
為would_block
-
類型 | 非阻塞行為 | 立即返回? | 處理方式 |
---|---|---|---|
send() | ? 支持 | 是 | |
write_some() | ? 支持 | 是 | 不能寫入全部就返回部分或拋錯 |
write() | ?? 不建議用于非阻塞 | ? 否 | 會循環調用 write_some 阻塞等待 |
recv() | ? 支持 | 是 | 沒數據就拋 would_block |
read_some() | ? 支持 | 是 | 讀部分數據或拋 would_block |
read() | ?? 不建議非阻塞使用 | ? 否 | 會阻塞直到 buffer 滿或報錯 |
如何設置非阻塞模式
socket.non_blocking(true);
flags
Boost.Asio 的 send()
和 receive()
函數提供了 flags
參數,用來設置底層 BSD socket API 的行為?
在 Boost.Asio 中通過枚舉類:
boost::asio::socket_base::message_flags
一些標志 :
Boost.Asio 枚舉名 | 底層等效宏 | 含義說明 |
---|---|---|
boost::asio::socket_base::message_peek | MSG_PEEK | 查看數據但不消費 |
boost::asio::socket_base::message_out_of_band | MSG_OOB | 緊急數據(帶外) |
boost::asio::socket_base::message_do_not_route | MSG_DONTROUTE | 不走路由表 |
boost::asio::socket_base::message_end_of_record | MSG_EOR | 表示數據為一條完整記錄(少用) |