boost asio同步编程(附源码api)

首先注明,这里我写的都是关于tcp的通信。

通信大致流程

创建端点

创建tcp端点的api是boost::asio::ip::tcp::endpoint;

当然创建udp端点的api则是boost::asio::ip::udp::endpoint;

是一个表示 TCP/UDP 端点的类,在 Boost.Asio 库中用于网络编程。它通常用于指定要连接的远程主机和端口,或者表示在本地主机上监听的端点

其中endpoint的构造函数使用得较多是参数为网络类型的ip地址端口号。源码构造如下。

这里的网络类型的ip地址需要解释以下。平常我们说得ip地址都是点分十进制的,而我们在网络通信中实际需要的ip地址则是需要进行网络序列化的ip地址。因此我们需要将点分十进制的ip地址转化成为网络序列的ip地址。这里需要使用的转化函数api则是boost::asio::ip::address::from_string。

对于这个转化ip地址序列的函数源码如下

其中第二个参数是一个boost::system::error_code类型的变量。

介绍完这些api之后,下边提供一个完整的创建客户端和服务端endpoint的demo。

void client_EndPoint() {string raw_ip_address = "127.0.0.1";unsigned short port_num = 3333;boost::system::error_code ec;boost::asio::ip::address ip_address= boost::asio::ip::address::from_string(raw_ip_address, ec);if (ec.value()) {cerr << "transfered failed" << endl;cout << "Error code: " << ec.value() <<"ec Message is" << ec.message() << endl;return ec.value();}//生成端点。该端点由ip地址和端口号生成boost::asio::ip::tcp::endpoint ep(ip_address, port_num);
}
/*boost::asio::ip::address_v4::any()是一个Boost.Asio库中的函数,用于创建一个表示
IPv4任意地址(通配地址)的对象。这个函数返回一个特殊的address_v4对象,它代表了网络
中的任何IPv4地址。这个函数通常用于初始化socket绑定到一个通配地址,这表示socket应该
接受进入的连接,不论这些连接来自于哪个IP地址。*/
int server_EndPoint() {//服务器端口号unsigned short port_num = 3333;//服务器地址boost::asio::ip::address ip_address = boost::asio::ip::address_v4::any();//生成一个tcp用来通信的端点boost::asio::ip::tcp::endpoint ep(ip_address, port_num);return 0;
}

创建套接字

asio空间中,我们首先不可避免的就是类io_service或io_context。

注意,io_context这个类是用来替代io_service的,所以建议以后都直接使用io_context即可

这个类非常重要,它相当于我们程序与系统之间I/O操作的中介,我们所有的接受或发送数据操作,都是通过将需求提交给这个类,然后这个类再交给计算机来执行的。

客户端创建socket

int create_tcp_socket() {//创建上下文boost::asio::io_context ioc;//创建ipv4的协议boost::asio::ip::tcp protocol = boost::asio::ip::tcp::v4();//创建socketboost::asio::ip::tcp::socket sock(ioc);//打开socketboost::system::error_code ec;sock.open(protocol, ec);return 0;
}

这里构造的socket并没有被开启,即只有在被open,在被连接或者接收才能发送数据。构造源码如下

这里的sock.open()函数是 Boost.Asio 库中的一个函数,用于打开一个 TCP 套接字。源码如下,

服务端创建acceptor

下边是一个服务端的创建欢迎套接字demo

int create_acceptor_socket() {/*旧写法*/创建上下文//boost::asio::io_context ioc;生成acceptor服务//boost::asio::ip::tcp::acceptor acceptor(ioc);创建ipv4的协议//boost::asio::ip::tcp protocol = boost::asio::ip::tcp::v4();打开socket//boost::system::error_code ec;//acceptor.open(protocol, ec);//if (ec.value()) {//	cerr << "open failed" << "Error Code : " << ec.value()//		<< "Error Message : " << ec.message() << endl;//}/*新写法*///创建上下文boost::asio::io_context ioc;//生成acceptor服务,(指定地址和端口,实现一个默认绑定)boost::asio::ip::tcp::acceptor acceptor(ioc, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 3333));return 0;
}

第一种构造acceptor的源码如下。这样构造出来的acceptor并没有启动acceptor去监听新的连接。在acceptor能够监听新的连接之前,必须先调用open函数,启动acceptor去监听新的连接。

第二种构造acceptor的方式,会在构造acceptor的时候自动去启动acceptor在指定的端点监听新的连接

服务器绑定套接字

int bind_acceptor_socket() {//创建服务boost::asio::io_context ioc;unsigned short port_num = 3333;//创建端点boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address_v4::any(), port_num);//生成accpetor,并手动绑定boost::asio::ip::tcp::acceptor acceptor(ioc, ep.protocol());//绑定端点,返回错误码boost::system::error_code ec;acceptor.bind(ep, ec);if (ec.value()) {cerr << "open failed" << "Error Code : " << ec.value()<< "Error Message : " << ec.message() << endl;}return 0;
}

boost::asio::ip::address_v4::any() 是一个Boost.Asio库中的函数,用于创建一个表示IPv4任意地址(通配地址)的address_v4对象。这个函数返回一个特殊的address_v4对象,它可以绑定到一个网络接口上,表示接受任意IPv4地址的数据包

这里构造endpoint的方法也会自动去开启open。源码如下

这段代码需要好好理解下。这里的构造的endpoint端点返回的是一个通配地址,即一个通配的绑定端口的端点,它并没有绑定任何一台主机。这里构造的acceptor仅仅是一个只能接收ipv4协议的acceptor的默认绑定(本地ip,但是并不清楚在哪个端口)。所以需要使用acceptor的bind方法绑定指定的端点(拿到里边的端口)。

这里可能会有疑惑,普通套接字和欢迎套接字的bind()和open()的先后顺序。是先bind再open。这里构造endpoint的方法也会自动去开启open。但是它还并没有去bind,所以并不会及时去开启open,而是等到bind之后再去开启,但是这里并不需要我们去操作,而是程序自动完成的。

客户端连接

客户端的连接比较简单,就先提供代码,再做解读。

int client_connect()
{//服务器地址string raw_ip_address = "192.168.1.124";//服务器端口unsigned short port_num = 3333;try{//创建上下文服务boost::asio::io_context ioc;//创建端点boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string(raw_ip_address), port_num);//创建socketboost::asio::ip::tcp::socket sock(ioc, ep.protocol());//sock.connect(ep);}catch(boost::system::system_error& e){cerr << "open failed" << "Error Code : " << e.code()<< "Error Message : " << e.what() << endl;}return 0;
}

首先构造出服务端的端点endpoint。再构造一个客户端的socket。这里使用构造socket的方法会将构造出来的socket自动开启,即只有被连接或者接收就能发送或接收数据。源码如下

使用客户端的socket的connect方法去连接服务器的端点即可。connet方法的源码如下

下边写一个服务端获取一个新的连接的demo

int accept_new_connection()
{const int BACKLOG_SIZE = 30;unsigned short port_num = 3333;//创建端点boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address_v4::any(), port_num);boost::asio::io_context ioc;try {//生成一个acceptorboost::asio::ip::tcp::acceptor acceptor(ioc, ep.protocol());//绑定端口acceptor.bind(ep);//监听acceptor.listen(BACKLOG_SIZE);//再创建一个socketboost::asio::ip::tcp::socket sock(ioc);//将acceptor接收新的连接,交给新创建的sock处理acceptor.accept(sock);}catch (boost::system::system_error& e) {cerr << "open failed" << "Error Code : " << e.code()<< "Error Message : " << e.what() << endl;}return 0;
}

boost::asio中的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>
结构如下

buffer的重要性

boost中的send接口要求参数为ConstBufferSequence类型。所以我们需要常用ConstBufferSequence类型的参数,但是怎么去构造这样类型的参数呢?下边是send函数源码

使用boost::asio中的const_buffer去构造ConstBufferSequence

void use_const_buffer()
{string buf("hello boost");//构造一个asio中的一个const_buffer对象boost::asio::const_buffer asio_buff(buf.c_str(), buf.size());vector<boost::asio::const_buffer> bufSequece;bufSequece.emplace_back(asio_buff);
}

可以看出这样写太麻烦了。asio中提供了buffer函数转化成为send函数参数需要的类型。

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

buffer函数该函数接收多种形式的字节流,该函数返回asio::mutable_buffers_1 或者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函数的参数使用。可以参考下边这个demo。

void use_buffer_array()
{const size_t BUF_SIZE_BYTES = 20;std::unique_ptr<char[]> buf(new char[BUF_SIZE_BYTES]);//static_cast <type-id>( expression )。//static_cast把expression转换为type-id类型boost::asio::mutable_buffers_1 input_buf =boost::asio::buffer(static_cast<void*>(buf.get()), BUF_SIZE_BYTES);}

下边是buffer函数的源码。第一个参数是字符串的首地址,第二个参数是字符串的长度。

同步读写api

同步写

write_some

write_some可以每次向指定的空间写入固定的字节数。这个函数将会阻塞的写入,直到一个或者更多的数据字节成功写入,另或是出现错误。这是源码给出的解释,也能这样的理解,就是write_some可以每次向指定的空间写入固定的字节数,如果写缓冲区满了,就只写一部分,返回写入的字节数

demo如下

void write_some_to_socket(boost::asio::ip::tcp::socket& sock)
{string buf = "hello boost";size_t total_bytes_written = 0;//循环发送while (total_bytes_written != buf.size()) {//注意这里写的位置要加上偏移量,更符合实际total_bytes_written += sock.write_some(boost::asio::buffer(buf.c_str() + total_bytes_written,buf.size() - total_bytes_written));}}

write_some函数源码如下

send

这个函数将会阻塞的写入,直到一个或者更多的数据字节成功写入,另或是出现错误。这也是源码给出的解释。但是send函数会一次性将buffer中的内容发送给对端,如果有部分字节因为发送缓冲区满无法发送,则阻塞等待,直到发送缓冲区可用,则继续发送完成,这样理解会更好

demo如下

int send_to_socket()
{std::string raw_ip_address = "127.0.0.1";unsigned short port_num = 3333;try {boost::asio::ip::tcp::endpointep(boost::asio::ip::address::from_string(raw_ip_address),port_num);boost::asio::io_service ios;// 建立套接字boost::asio::ip::tcp::socket sock(ios, ep.protocol());sock.connect(ep);std::string buf = "Hello World!";/*下边的send_length只有三种情况。1、大于0,即等于buff的长度2、等于0,表示对端关闭3、小于0,表示socket出现系统级错误*/int send_length = sock.send(boost::asio::buffer(buf.c_str(), buf.length()));if (send_length <= 0) {cout << "send failed" << endl;return 0;}}catch (boost::system::system_error& e) {std::cout << "Error occured! Error code = " << e.code()<< ". Message: " << e.what();return e.code().value();}return 0;
}

send函数源码如下

write

可以一次性将所有数据发送给对端,如果发送缓冲区满了则阻塞,直到发送缓冲区可用,将数据发送完成

demo如下

int write_to_socket()
{std::string raw_ip_address = "127.0.0.1";unsigned short port_num = 3333;try {boost::asio::ip::tcp::endpointep(boost::asio::ip::address::from_string(raw_ip_address),port_num);boost::asio::io_service ios;// 建立套接字boost::asio::ip::tcp::socket sock(ios, ep.protocol());sock.connect(ep);std::string buf = "Hello World!";/*下边的send_length只有三种情况。1、大于0,即等于buff的长度2、等于0,表示对端关闭3、小于0,表示socket出现系统级错误*/int send_length = boost::asio::write(sock, boost::asio::buffer(buf.c_str(), buf.length()));if (send_length <= 0) {cout << "send failed" << endl;return 0;}}catch (boost::system::system_error& e) {std::cout << "Error occured! Error code = " << e.code()<< ". Message: " << e.what();return e.code().value();}return 0;
}

write函数源码如下

同步读

read_some

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

demo如下

string read_some_fromSocket(boost::asio::ip::tcp::socket& sock)
{const unsigned char MESSAGE_SIZE = 7;char buf[MESSAGE_SIZE];size_t total_bytes_read = 0;while (total_bytes_read != MESSAGE_SIZE) {total_bytes_read += sock.read_some(boost::asio::buffer(buf + total_bytes_read,MESSAGE_SIZE - total_bytes_read));}return string(buf, total_bytes_read);
}

read_some函数源码如下

receive

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

demo如下

int receive_fromSocket(boost::asio::ip::tcp::socket& sock)
{std::string raw_ip_address = "127.0.0.1";unsigned short port_num = 3333;try {boost::asio::ip::tcp::endpointep(boost::asio::ip::address::from_string(raw_ip_address),port_num);boost::asio::io_service ios;boost::asio::ip::tcp::socket sock(ios, ep.protocol());sock.connect(ep);const unsigned char BUFF_SIZE = 7;char buffer_receive[BUFF_SIZE];int receive_length = sock.receive(boost::asio::buffer(buffer_receive, BUFF_SIZE));if (receive_length <= 0) {cout << "receive failed" << endl;}}catch (boost::system::system_error& e) {std::cout << "Error occured! Error code = " << e.code()<< ". Message: " << e.what();return e.code().value();}return 0;
}

receive函数源码如下

read

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

demo如下

int read_fromSocket(boost::asio::ip::tcp::socket& sock)
{std::string raw_ip_address = "127.0.0.1";unsigned short port_num = 3333;try {boost::asio::ip::tcp::endpointep(boost::asio::ip::address::from_string(raw_ip_address),port_num);boost::asio::io_service ios;boost::asio::ip::tcp::socket sock(ios, ep.protocol());sock.connect(ep);const unsigned char BUFF_SIZE = 7;char buffer_receive[BUFF_SIZE];int receive_length = boost::asio::read(sock, boost::asio::buffer(buffer_receive, BUFF_SIZE));if (receive_length <= 0) {cout << "receive failed" << endl;}}catch (boost::system::system_error& e) {std::cout << "Error occured! Error code = " << e.code()<< ". Message: " << e.what();return e.code().value();}return 0;
}

read函数源码如下

下边提供一个同步读写的echo服务器和客户端,源码如下

syncSer:codes-C++: C++学习 - Gitee.com

syncCli:codes-C++: C++学习 - Gitee.com

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

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

相关文章

鸿蒙ArkUI:【编程范式:命令式->声明式】

命令式 简单讲就是需要开发用代码一步一步进行布局&#xff0c;这个过程需要开发全程参与。 开发前请熟悉鸿蒙开发指导文档&#xff1a;gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到。 Objective-C ObjectiveC 复制代码 UIView *cardView …

day3_prefixSum

一、前缀和技巧 重点 前缀和技巧适用于快速、频繁地计算一个索引区间内的元素之和 个人理解&#xff1b;预计算&#xff0c;空间换时间 1.(一维数组的前缀和)303区域和检索-数组不可变 获取闭区间值 [left,right] -> preSum[right 1] - preSum[left],其中preSum[right…

Linux下VMamba 环境复现+环境测试

# 1. 创建自己的虚拟环境 conda create -n VMamba python3.10.13 conda activate VMamba # 2. cuda-11.8 conda install cudatoolkit11.8 -c nvidia # 3. torch torchvision torchaudio 与 官网命令一致 pip install torch2.1.1 torchvision0.16.1 torchaudio2.1.1 --index-url…

苹果电脑怎么清内存?2024有哪些好用的工具?

在使用苹果电脑的过程中&#xff0c;我们可能会遇到系统运行缓慢、程序响应迟缓或频繁出现应用程序崩溃的情况&#xff0c;这些问题很可能是由于内存占用过高所导致。内存&#xff0c;或称为RAM&#xff08;RandomAccessMemory&#xff09;&#xff0c;是计算机的临时存储区&am…

超级好看的html网站维护源码

源码介绍 好看的html网站维护源码&#xff0c;源码由HTMLCSSJS组成&#xff0c;记事本打开源码文件可以进行内容文字之类的修改&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器里面&#xff0c; 源码截图 源码下载 好看的html网站维护源码

py黑帽子学习笔记_网络编程工具

tcp客户端 socket.AF_INET表示使用标准IPV4地址和主机名 SOCK_STREAM表示这是一个TCP客户端 udp客户端 udp无需连接&#xff0c;因此不需要client.connect这种代码 socket.SOCK_DGRAM是udp的 tcp服务端 server.listen(5)表示设置最大连接数为5 发现kill server后端口仍占用…

【服务治理中间件】consul介绍和基本原理

目录 一、CAP定理 二、服务注册中心产品比较 三、Consul概述 3.1 什么是Consul 3.2 Consul架构 3.3 Consul的使用场景 3.4 Consul健康检查 四、部署consul集群 4.1 服务器部署规划 4.2 下载解压 4.3 启动consul 五、服务注册到consul 一、CAP定理 CAP定理&#xff…

i春秋-Backdoor

题目 考点 git源码泄露 Linux文件恢复 代码审计 http 解题 参考wp https://blog.csdn.net/cbhjerry/article/details/105791056https://www.pianshen.com/article/19461342501/扫描 题目给出提示&#xff1a;敏感文件泄漏 于是使用dirsearch扫一下 python dirsearch.py -…

ICode国际青少年编程竞赛- Python-4级训练场-while语句综合

ICode国际青少年编程竞赛- Python-4级训练场-while语句综合 1、 for i in range(4):while not Flyer[i].disappear():wait()Spaceship.step(6)Spaceship.turnLeft()2、 Dev.turnLeft() for i in range(4):Spaceship.step(2)while Flyer[i].disappear():wait()Dev.step(4)Dev.…

Failed to parse source map (@toast-ui/editor/dist/purify.js.map)

使用 toast-ui-editor 时出现报错&#xff1a;Failed to parse source map (toast-ui/editor/dist/purify.js.map) 解决方法很简单&#xff1a; "start": "set "GENERATE_SOURCEMAPfalse" && react-scripts start ",在启动脚本时添加执…

程序员代码面试指南题目解析(一)

题目一&#xff1a;如何仅用递归函数和栈操作逆序一个栈 题目要求&#xff1a; 一个栈依次压入 1、2、3、4、5&#xff0c;那么从栈顶到栈底分别为5、4、3、2、1。将这个栈 转置后&#xff0c;从栈顶到栈底为 1、2、3、4、5&#xff0c;也就是实现栈中元素的逆序&#xff0c;但…

【吴恩达机器学习-week2】多个变量的线性回归问题

文章目录 1.1 目标2 问题陈述2.1 包含我们示例的矩阵 X2.2 参数向量 w \mathbf{w} w 和 b b b 3 使用多个变量进行模型预测3.1 逐元素单独预测3.2 单一预测&#xff0c;向量 4 使用多个变量计算成本5 使用多个变量的梯度下降5.1 使用多个变量计算梯度 多个变量的梯度下降小结…

搜歌网搜索各种类型音乐,统统歌曲转换格式mp3,轻松实现音乐自由!

在互联网的广阔天地中&#xff0c;音乐爱好者们总能找到满足自己需求的平台。其中&#xff0c;支持全网搜歌的网站无疑是一个值得推荐的音乐探索乐园。无论是寻找经典老歌&#xff0c;还是发掘新兴音乐&#xff0c;搜他们都能为音乐爱好者提供一站式的服务。 一般支持全网搜索…

值得收藏!!《软考信息处理技术员》必背100母题,轻松45+

距离软考考试的时间越来越近了&#xff0c;趁着这两周赶紧准备起来 今天给大家整理了——软考信息处理技术员100道经典母题&#xff0c;年年从里面抽&#xff0c;有PDF&#xff0c;可打印&#xff0c;每天刷几道。 第一章 电脑的基本操作 1、&#xff08; &#xff09;不是国产…

Linux线程(二)线程互斥

目录 一、为什么需要线程互斥 二、线程互斥的必要性 三、票务问题举例&#xff08;多个线程并发的操作共享变量引发问题&#xff09; 四、互斥锁的用法 1.互斥锁的原理 2、互斥锁的使用 1、初始化互斥锁 2、加锁和解锁 3、销毁互斥锁&#xff08;动态分配时需要&#…

RFID在汽车制造中的应用如何改变行业

随着工业4.0和中国制造2025的推进&#xff0c;企业对于智能化、自动化的需求日益增长&#xff0c;RFID射频技术在制造业中已经相当普遍了。在如今这瞬息万变的行业与时代中&#xff0c;RFID技术可以帮助企业获得竞争优势&#xff0c;简化日益复杂的生产流程&#xff0c;推动企业…

C语言实战项目---通讯录

项目要实现的内容&#xff1a;能够存放100个人的通讯录程序&#xff0c;能够实现联系人数据的存储&#xff0c;删除&#xff0c;修改&#xff0c;查找&#xff0c;展示联系人的信息。 所需知识&#xff1a;结构体&#xff0c;指针&#xff0c;函数................. 废话不多…

2016-2021年全国范围的2.5m分辨率的建筑屋顶数据

一、论文介绍 摘要&#xff1a;大规模且多年的建筑屋顶面积&#xff08;BRA&#xff09;地图对于解决政策决策和可持续发展至关重要。此外&#xff0c;作为人类活动的细粒度指标&#xff0c;BRA可以为城市规划和能源模型提供帮助&#xff0c;为人类福祉带来好处。然而&#xf…

Qt之常用控件一

Widget常见属性及其作用 属性作用enabled 设置控件是否可使⽤. true 表⽰可⽤, false 表⽰禁⽤ geometry 位置和尺⼨. 包含 x, y, width, height 四个部分. 其中坐标是以⽗元素为参考进⾏设置的. windowTitle 设置 widget 标题 windowIcon 设置 widget 图标 windowOpa…

java日历类概述

Java中的Calendar类位于java.util包下&#xff0c;它是一个抽象类&#xff0c;用于表示和管理日期及时间。Calendar类并不是直接实例化的&#xff0c;而是通过其提供的静态方法来获取实例。通常情况下&#xff0c;当你尝试创建一个Calendar实例时&#xff0c;实际上你得到的是G…