news 2026/2/9 20:56:01

C++ asio网络编程(2) buffer同步读写

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++ asio网络编程(2) buffer同步读写

一、buffer是什么

任何网络库都有提供buffer的数据结构,所谓buffer就是接收和发送数据时缓存数据的结构。
boost::asio提供了asio::mutable_buffer 和 asio::const_buffer这两个结构,他们是一段连续的空间,首字节存储了后续数据的长度。
asio::mutable_buffer用于写服务,asio::const_buffer用于读服务。但是这两个结构都没有被asio的api直接使用。
对于api的buffer参数,asio提出了MutableBufferSequence和ConstBufferSequence概念,他们是由多个asio::mutable_buffer和asio::const_buffer组成的。也就是说boost::asio为了节省空间,将一部分连续的空间组合起来,作为参数交给api使用。
我们可以理解为MutableBufferSequence的数据结构为std::vector<asio::mutable_buffer>

结构如下:

每隔vector存储的都是mutable_buffer的地址,每个mutable_buffer的第一个字节表示数据的长度,后面跟着数据内容。
这么复杂的结构交给用户使用并不合适,所以asio提出了buffer()函数,该函数接收多种形式的字节流,该函数返回asio::mutable_buffers_1 o或者asio::const_buffers_1结构的对象。
如果传递给buffer()的参数是一个只读类型,则函数返回asio::const_buffers_1 类型对象。
如果传递给buffer()的参数是一个可写类型,则返回asio::mutable_buffers_1 类型对象。
asio::const_buffers_1和asio::mutable_buffers_1是asio::mutable_buffer和asio::const_buffer的适配器,提供了符合MutableBufferSequence和ConstBufferSequence概念的接口,所以他们可以作为boost::asio的api函数的参数使用。

简单概括一下,我们可以用buffer()函数生成我们要用的缓存存储数据。
比如boost的发送接口send要求的参数为ConstBufferSequence类型

//ConstBufferSequence 常量缓冲区序列类型 template<typename ConstBufferSequence> std::size_t send(const ConstBufferSequence & buffers);

1.利用buffer发送数据

void use_const_buffer() { std::string buf = "hello boost"; //转换为const_buffer类型 buf.c_str()为字符串首地址,buf.length()字符串的长度 asio::const_buffer asio_buf(buf.c_str(), buf.length()); //构建缓冲区序列(vector 容器存储多个 const_buffer) std::vector<asio::const_buffer> buffers_sequence; // // 将单个 const_buffer 添加到缓冲区序列中 // 实际场景中可添加多个不同的 const_buffer,发送时会按顺序拼接所有缓冲区数据 buffers_sequence.push_back(asio_buf); }

下面这种方法可以直接用buffer函数转化为send需要的参数类型

void use_buffer_str() { asio::const_buffer output_buf = asio::buffer("hello world"); }

我们也可以将数组转化为send接受的类型

void use_buffer_array() { const size_t BUF_SIZE_BYTES = 20; std::unique_ptr<char[]>buf(new char[BUF_SIZE_BYTES]); auto input_buf = asio::buffer(static_cast<void*>(buf.get()), BUF_SIZE_BYTES); }

二、asio socket同步读写

同步写

1.同步写write_some

boost::asio提供了几种同步写的api,write_some可以每次向指定的空间写入固定的字节数,如果写缓冲区满了,就只写一部分,返回写入的字节数。

void wirte_to_socket(asio::ip::tcp::socket& sock) { std::string buf = "Hello world"; //total_bytes_w是已发送的字节数 std::size_t total_bytes_w = 0; //循环发送 //write_som 返回每次写入的字节数 while (total_bytes_w != buf.length()) { total_bytes_w += sock.write_some(asio::buffer(buf.c_str() + total_bytes_w, buf.length() - total_bytes_w)); } }

2.同步写send

write_some使用起来比较麻烦,需要多次调用,asio提供了send函数。send函数会一次性将buffer中的内容发送给对端,如果有部分字节因为发送缓冲区满无法发送,则阻塞等待,直到发送缓冲区可用,则继续发送完成。

int send_data_by_send() { std::string raw_ip_address = "192.168.3.11"; unsigned short port_num = 6666; try { asio::ip::tcp::endpoint ep(asio::ip::make_address(raw_ip_address), port_num); asio::io_context ioc; asio::ip::tcp::socket sock(ioc, ep.protocol()); sock.connect(ep); std::string buf = "Hello world"; int send_length = sock.send(buf.c_str(), buf.length()); if (send_length <= 0) { return 0; } } catch (system::system_error& e) { std::cout << "Error code = " << e.code() << ". Message: " << e.what(); return e.code().value(); } }

3.同步写write

类似send方法,asio还提供了一个write函数,可以一次性将所有数据发送给对端,如果发送缓冲区满了则阻塞,直到发送缓冲区可用,将数据发送完成。

int send_data_by_write() { std::string raw_ip_address = "192.168.3.11"; unsigned short port_num = 6666; try { asio::ip::tcp::endpoint ep(asio::ip::make_address(raw_ip_address), port_num); asio::io_context ioc; asio::ip::tcp::socket sock(ioc, ep.protocol()); sock.connect(ep); std::string buf = "Hello world"; int send_length = asio::write(sock,asio::buffer(buf.c_str(), buf.length())); if (send_length <= 0) { return 0; } } catch (system::system_error& e) { std::cout << "Error code = " << e.code() << ". Message: " << e.what(); return e.code().value(); } }

同步读

1.同步读read_some

同步读和同步写类似,提供了读取指定字节数的接口read_some

std::string read_from_socket(asio::ip::tcp::socket& sock) { const unsigned char MESSAGE_SIZE = 7; char buf[MESSAGE_SIZE]; std::size_t total_bytes_read = 0; while (total_bytes_read != MESSAGE_SIZE) { total_bytes_read += sock.read_some(asio::buffer(buf + total_bytes_read, MESSAGE_SIZE - total_bytes_read)); } return std::string(buf, total_bytes_read); } int read_data_byread_some() { std::string raw_ip_address = "192.168.3.11"; unsigned short port_num = 6666; try { asio::ip::tcp::endpoint ep(asio::ip::make_address(raw_ip_address), port_num); asio::io_context ioc; asio::ip::tcp::socket sock(ioc, ep.protocol()); sock.connect(ep); read_from_socket(sock); } catch (system::system_error& e) { std::cout << "Error code = " << e.code() << ". Message: " << e.what(); return e.code().value(); } }

2.同步读receive

可以一次性同步接收对方发送的数据

int read_data_by_recevie() { std::string raw_ip_address = "192.168.3.11"; unsigned short port_num = 6666; try { asio::ip::tcp::endpoint ep(asio::ip::make_address(raw_ip_address), port_num); asio::io_context ioc; asio::ip::tcp::socket sock(ioc, ep.protocol()); sock.connect(ep); const unsigned char Buff_SIZE = 7; char buff_recevie[Buff_SIZE]; int receive_length = sock.receive(asio::buffer(buff_recevie, Buff_SIZE)); if (receive_length <= 0) { std::cout << "receive failed" << std::endl; } } catch (system::system_error& e) { std::cout << "Error code = " << e.code() << ". Message: " << e.what(); return e.code().value(); } }

3.同步读read

可以一次性同步读取对方发送的数据

int read_data_by_read() { std::string raw_ip_address = "192.168.3.11"; unsigned short port_num = 6666; try { asio::ip::tcp::endpoint ep(asio::ip::make_address(raw_ip_address), port_num); asio::io_context ioc; asio::ip::tcp::socket sock(ioc, ep.protocol()); sock.connect(ep); const unsigned char Buff_SIZE = 7; char buff_recevie[Buff_SIZE]; int receive_length = asio::read(sock,asio::buffer(buff_recevie, Buff_SIZE)); if (receive_length <= 0) { std::cout << "receive failed" << std::endl; } } catch (system::system_error& e) { std::cout << "Error code = " << e.code() << ". Message: " << e.what(); return e.code().value(); } }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/9 0:38:12

原神祈愿数据分析完整指南:告别手动记录,精准掌握抽卡概率

作为原神玩家&#xff0c;你是否曾为这些问题困扰&#xff1a;抽卡时记不清离保底还有多少发&#xff1f;想回顾某个五星角色什么时候抽到的&#xff1f;换设备后所有抽卡历史都丢失了&#xff1f;现在&#xff0c;genshin-wish-export工具将彻底解决这些烦恼&#xff0c;让你真…

作者头像 李华
网站建设 2026/2/5 2:45:49

基于Java的超市购物商城采购销存系统41f0q511

目录已开发项目效果实现截图开发技术系统开发工具&#xff1a;核心代码参考示例1.建立用户稀疏矩阵&#xff0c;用于用户相似度计算【相似度矩阵】2.计算目标用户与其他用户的相似度系统测试总结源码文档获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&…

作者头像 李华
网站建设 2026/2/7 16:01:50

写给学弟学妹的编程心语:不是捷径,是心路

写给学弟学妹的编程心语&#xff1a;不是捷径&#xff0c;是心路凌晨三点&#xff0c;屏幕的光还亮着。这是第几次对着一段报错代码陷入沉思&#xff1f;那些看似简单的教程&#xff0c;为什么一到自己手里就变得如此陌生&#xff1f;如果你也曾这样怀疑过自己&#xff0c;请相…

作者头像 李华
网站建设 2026/2/9 7:46:33

01-Ansible 自动化介绍

文章目录 01-Ansible 自动化介绍Ansible 自动化介绍手动执行任务和自动化执行任务基础架构即代码Ansible 与 DevOps什么是 ANSIBLE&#xff1f;Ansible 特点Ansible 概念和架构Ansible WayAnsible 用例 Ansible 部署准备实验环境控制节点受管节点LinuxWindows网络设备网络设备 …

作者头像 李华
网站建设 2026/2/7 1:29:46

窗口置顶神器:如何让重要窗口永远保持在最前端?

窗口置顶神器&#xff1a;如何让重要窗口永远保持在最前端&#xff1f; 【免费下载链接】AlwaysOnTop Make a Windows application always run on top 项目地址: https://gitcode.com/gh_mirrors/al/AlwaysOnTop 在日常电脑使用中&#xff0c;你是否经常为了找到被其他窗…

作者头像 李华