Boost.Asio学习(2):同步读写

Asio 的 buffer 是什么?

boost::asio::buffer(...) 是一个函数模板,用于创建一个通用的 buffer 对象,可传递给 I/O 函数(如 read, write, read_some, write_some 等)。

它返回的是 mutable_bufferconst_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_bufferconst_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::stringbuffer(str)✅ 安全
std::vectorbuffer(vec)✅ 安全
char[100]buffer(arr)✅ 自动推导
char* ptrbuffer(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_codewould_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_peekMSG_PEEK查看数据但不消费
boost::asio::socket_base::message_out_of_bandMSG_OOB紧急数据(带外)
boost::asio::socket_base::message_do_not_routeMSG_DONTROUTE不走路由表
boost::asio::socket_base::message_end_of_recordMSG_EOR表示数据为一条完整记录(少用)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/90010.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/90010.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java中如何枚举正则表达式捕获组的名字

在使用正则表达式在匹配文本时&#xff0c;除了可以通过表达式捕获命中的文本串外&#xff0c;还可以对捕获的文本串进行命名。尤其是在解析日志的场景中&#xff0c;经常会被用到。表达式如下&#xff1a; \<(?<pri>\d)\>(?<time>.*) (?<host>\S)…

CentOS 系统高效部署 Dify 全攻略

系列文章目录 CentOS系统高效部署fastGPT全攻略 文章目录 系列文章目录一、前言二、准备工作与系统要求三、安装 Docker 与 Docker Compose四、部署 Dify 核心服务五、数据库与存储配置六、网络与安全优化七、监控与运维八、升级与扩展九、附录与资源关键命令速查表官方文档…

xyctf2025第三届京麒CTF

一.MISC 1.XGCTF 直接ai搜索一遍找到了出题人的博客LamentXU 2024-2025年终总结 - LamentXU - 博客园 知道了原题是ciscn中的 在LamentXU的博客乱逛Patriot CTF 2024 MISC 部分 wp - LamentXU - 博客园 找到了博客网站,让后搜索ciscn扎到了博客 CISCN华东南WEB-Polluted |…

Python爬虫 模拟登录 requests版

前言 网站必须是登录状态才能查看网站信息,是最常见的反爬手段,下面我分享一下request模拟登录状态进行请求 目录 模拟登录的原理 直接复制网站Cookie模拟登录状态 通过登录接口信息破解出Cookie模拟登录状态 模拟登录的原理 网站是使用Cookie和session记录网站的登录状态…

一些改进策略

1.要计算一个神经网络模型的总参数量、可训练参数量以及计算量&#xff08;FLOPs&#xff09;&#xff0c;可以使用以下步骤&#xff1a; ### 计算总参数量和可训练参数量&#xff1a; 1. **逐层计算参数量**&#xff1a; - 对于每一层&#xff0c;确定该层的参数量。这通…

React Native响应式布局实战:告别媒体查询,拥抱跨屏适配新时代

前言:当设计师说"这个页面要适配所有手机和平板…" “什么?React Native不支持CSS媒体查询?那怎么实现响应式布局?”——这是很多刚接触React Native的开发者会遇到的灵魂拷问。 但别慌,没有@media,我们照样能玩转多端适配!想象一下:你的App在iPhone SE的小…

[Java恶补day39] 整理模板·考点六【反转链表】

考点六【反转链表】 【考点总结】 1. 206. 【题目】 【核心思路】 【复杂度】 时间复杂度&#xff1a; O ( ) O() O()。 空间复杂度&#xff1a; O ( ) O() O()。 【代码】 92. 【题目】 【核心思路】 【复杂度】 时间复杂度&#xff1a; O ( ) O() O()。 空间复杂度&a…

7,TCP服务器

1,创建一个工程 文件目录:

Modbus_TCP_V5 新功能

odbus TCP 服务器指令 MB_SERVER V5.0 新功能概述 如下图1所示服务器指令 MB_SERVER 从 V5.0 以后增加了三个新功能&#xff0c;分别为&#xff1a; 访问数据块中的数据区域&#xff0c;而不是直接访问 MODBUS 地址过程映像区的读访问限制统计变量 NDR_immediate 和 DR_immed…

2-RuoYi-UI管理平台的启动

RuoYi-UI是RuoYi后端框架的管理中心(基于 Vue.js 的前端项目)的详细配置与启动指南,结合官方文档和常见实践整理,涵盖环境准备、配置修改、启动流程及问题排查。 ⚙️ 一、环境准备 Node.js 版本要求:≥12.0(推荐 ≥14.0 或 18.18+ 适配 Vue3) 安装后验证: node -v …

WPF学习笔记(21)ListBox、ListView与控件模板

ListBox与控件模板 一、 ListBox默认控件模板详解二、ItemsPresenter集合数据呈现1. 概述2. 示例 三、 ListView默认控件模板详解1. 概述2. 示例 一、 ListBox默认控件模板详解 WPF 中的大多数控件都有默认的控件模板。 这些模板定义了控件的默认外观和行为&#xff0c;包括控…

操作系统【2】【内存管理】【虚拟内存】【参考小林code】

本文完全参考 虚拟内存内存分段内存分页段页式内存管理Linux内存管理 一、虚拟内存 1. 单片机的绝对物理地址 以单片机作为引子&#xff0c;它没有操作系统&#xff0c;每次写完程序是借助工具将程序烧录进单片机&#xff0c;程序才能运行。 单片机由于没有操作系统&#…

【王树森推荐系统】召回05:矩阵补充、最近邻查找

概述 这节课和后面几节课将详细讲述向量召回&#xff0c;矩阵补充是向量召回最简单的一种方法&#xff0c;不过现在已经不太常用这种方法了本节课的矩阵补充是为了帮助理解下节课的双塔模型上节课介绍了embedding&#xff0c;它可以把用户ID和物品ID映射成向量 矩阵补充 模型…

C# 事件(订阅事件和触发事件)

订阅事件 订阅者向事件添加事件处理程序。对于一个要添加到事件的事件处理程序来说&#xff0c;它必须具有 与事件的委托相同的返回类型和签名。 使用运算符来为事件添加事件处理程序&#xff0c;如下面的代码所示。事件处理程序位于该运 算符的右边。事件处理程序的规范可以…

64页精品PPT | 基于DeepSeek的数据治理方案AI大数据治理解决方案数据治理大模型解决方案

这份PPT文档是关于基于DeepSeek的大模型技术在数据治理方案中的应用介绍。强调数据作为新型生产要素与人工智能技术相结合的重要性&#xff0c;指出大模型凭借强大的文学理解、生成能力以及多模态数据处理能力&#xff0c;能为数据治理带来新机遇。文档详细阐述了数据资产化的路…

【文献笔记】Tree of Thoughts: Deliberate Problem Solving with Large Language Models

Tree of Thoughts: Deliberate Problem Solving with Large Language Models https://github.com/princeton-nlp/tree-of-thought-llm 标题翻译&#xff1a;思维树&#xff1a;利用大型语言模型问题求解 1. 内容介绍 1.1. 背景 决策过程有两种模式&#xff1a; 快速、自动…

使用 Mathematical_Expression 从零开始实现数学题目的作答小游戏【可复制代码】

从零开始实现数学题目的作答小游戏 开源技术栏 使用 Mathematical_Expression 让计算机做出题人&#xff0c;让代码与数学碰撞出趣味火花。 目录 文章目录 从零开始实现数学题目的作答小游戏目录 从零开始实现数学题目的作答小游戏&#xff1a;让代码与数学碰撞出趣味火花为…

DQL-9-小结

DQL-9-小结 小结DQL语句 小结 DQL语句 SELECT 字段列表 -- 字段名 [AS] 别名FROM 表名列表WHERE条件列表 -- >、 >、 <、 <、 、 <>、 like、 between ... and、 in、 and、 or, 分组之前过滤 GROUP BY分组字段列表HAVING分组后条件列表 -- 分组之后…

[netty5: WebSocketServerHandshaker WebSocketServerHandshakerFactory]-源码分析

在阅读这篇文章前&#xff0c;推荐先阅读以下内容&#xff1a; [netty5: WebSocketFrame]-源码分析[netty5: WebSocketFrameEncoder & WebSocketFrameDecoder]-源码解析 WebSocketServerHandshakerFactory WebSocketServerHandshakerFactory 用于根据客户端请求中的 Web…

数据挖掘:深度解析与实战应用

在当今数字化时代&#xff0c;数据挖掘已经成为企业获取竞争优势的关键技术之一。通过从大量数据中提取有价值的信息&#xff0c;企业可以更好地理解客户需求、优化业务流程、提高运营效率。本文将深入探讨数据挖掘的核心技术、实际应用案例以及如何在企业中实施数据挖掘项目。…