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 | 表示数据为一条完整记录(少用) |