🚀 [协程与异步服务器实战]:[C++20协程原理与Boost.Asio异步服务器开发]
📅 更新时间:2025年07月05日
🏷️ 标签:C++20 | 协程 | Boost.Asio | 异步编程 | 网络服务器

文章目录

  • 前言
  • 一、什么是协程?
  • 二、线程与协程?
  • 三、使用协程搭建异步服务器进行通信
    • 1.服务端
      • 1.流程图
      • 2.服务端代码
        • 1.主函数入口
        • 2.lisenter 协程函数
        • 3.echo协程函数
    • 2.客户端
  • 四、测试
  • 总结


前言

今天我们学习协程的基本概念,以及如何用协程来搭建一个简单的异步服务器来进行与客户端的收发数据


一、什么是协程?

协程(Coroutine)是一种比线程更轻量级的“并发编程”方式
它允许你在一个线程内,把任务分成多个可以挂起和恢复的小段,写出“像同步一样的异步代码”。

协程的特点
可以在执行过程中主动暂停(挂起),等条件满足后恢复执行
多个协程可以在同一个线程内切换,切换速度非常快
由程序员或框架调度,而不是操作系统

总结
协程就是可以随时挂起和恢复的轻量级任务,让你用很少的资源实现高效的并发和异步编程

二、线程与协程?

直观比喻
线程像“多个人各自做事”
协程像“一个人做多件事,可以随时暂停当前任务,去做别的,再回来继续”

三、使用协程搭建异步服务器进行通信

1.服务端

1.流程图

在这里插入图片描述

1.程序启动(main)
初始化 io_context
设置信号处理(监听 Ctrl+C 等信号,优雅退出)
启动 lisenter 协程

2.lisenter 协程
创建 acceptor,监听 10086 端口
进入无限循环:
异步等待新连接(co_await acceptor.async_accept()
每有一个新连接,启动一个 echo 协程处理该连接

3.echo 协程
进入无限循环:
异步读取客户端数据(co_await socket.async_read_some()
异步写回数据(co_await async_write()
信号处理
收到终止信号时,调用 ioc.stop(),优雅关闭服务器

2.服务端代码

1.主函数入口

我们在主函数中利用 try catch 来进行跑代码,这样可以防止后续的出错,
我们先定义一个信号集signal_set来实现服务器的优雅退出,当客户端使用Ctrl+C等操作的时候,我们可以通知 上下文 io_context直接调用 .stop() 来暂停服务

int main()
{try{boost::asio::io_context ioc(1);boost::asio::signal_set signals(ioc, SIGINT, SIGTERM);signals.async_wait([&](auto,auto){ioc.stop();});co_spawn(ioc,lisenter(),detached);//启动协程ioc.run();}catch(std::exception& e){std::cout << "main Exception is" << e.what() << std::endl;}
}

在写 Lambda 表达式的时候如果要调用上下文io_context必须用&捕获
核心原因是:io_context(以及很多 Asio 相关对象)本身禁止拷贝,只能被引用捕获,不能被值捕获

co_spawn(ioc,lisenter(),detached);//启动协程

co_spawn
co_spawn表示启动一个协程,参数分别为调度器执行的函数,以及启动方式, 比如我们启动了一个协程,deatched表示将协程对象分离出来,这种启动方式可以启动多个协程,他们都是独立的,如何调度取决于调度器,在用户的感知上更像是线程调度的模式,类似于并发运行,其实底层都是串行的

此时需要传入三个参数
第一个参数
boost::asio::io_context
所有异步事件和协程都要绑定到某个 io_context,它负责调度和执行

第二个参数
是你自定义的协程函数,返回类型通常是 awaitable<void>
这里我们自定义的函数是lisenter()

第三个参数
协程的完成方式
总共有三种,分别是
detached
作用:协程分离运行,主程序不关心协程的返回值和异常。
用法:适合“只管启动,不关心后续”的场景(如服务器监听、后台任务)

use_awaitable
作用:让协程的结果可以被 co_await 等待,用于协程之间的嵌套和组合。
用法:适合你想在另一个协程里等待这个协程的结果

最后一种是自定义的回调函数

所以我们这句话

co_spawn(ioc,lisenter(),detached);//启动协程

的完整意思就是
在 ioc 这个事件循环中,启动一个 lisenter 协程,让它自己运行,主程序不关心它的结果

2.lisenter 协程函数

我们定义一个协程函数,然后在协程函数中我们创建一个监听器acceptor进行绑定上下文tcp协议端口号

然后我们调用一个死循环,内部异步的进行监听然后创建一个socket,然后我们根据这个socket再创建一个协程echo单独管理此客户端的通信

awaitable<void> lisenter()
{auto executor = co_await this_coro::executor;//co_await异步获取调度器tcp::acceptor acceptor(executor, { tcp::v4(),10086 });for (;;){tcp::socket socket = co_await acceptor.async_accept(use_awaitable);co_spawn(executor, echo(std::move(socket)), detached);//为每一个连接单独启动 一个协程进行收发数据}
}

如果要写协程函数,必须是这种类型

awaitable<T>

我们在协程中进行了对当前协程获取调度器的实现

auto executor = co_await this_coro::executor;//co_await异步获取调度器

Boost.Asio 中,executor 是一个“执行环境”,负责调度和管理异步操作的执行
常见的 executorio_context::executor_type,它和 io_context 绑定

因为我们在主函数中使用了

co_spawn(ioc, lisenter(), detached)

启动协程时,协程会自动和 ioc 绑定
但在协程体内,如果你要创建新的异步对象(如 acceptor),需要明确告诉它用哪个 executor,否则它不知道该和哪个事件循环关联
所以相当于给这个监听器绑定了一个上下文io_context

然后我们再来介绍一下
co_await
co_awaitC++20 协程的通用关键字,它的作用是等待一个 “可等待对象” 完成,并获取其结果

比如这里我们用来获取当前协程的调度器和监听器分配的socket

auto executor = co_await this_coro::executor;
tcp::socket socket = co_await acceptor.async_accept(use_awaitable);
3.echo协程函数

这个协程函数就是单独为当前分配的客户端进行读写通信的实现

awaitable<void>echo(tcp::socket socket)
{try{char data[1024];for (;;){std::size_t n=co_await socket.async_read_some(boost::asio::buffer(data), use_awaitable);co_await async_write(socket, boost::asio::buffer(data, n), use_awaitable);}}catch (std::exception& e){std::cout << "echo Exception is" << e.what() << std::endl;}
}

我们多次利用这个co_await实现了将看似同步的代码,实现了异步等待的操作,比如这句

//获取收到数据长度
std::size_t n=co_await 
socket.async_read_some(boost::asio::buffer(data), use_awaitable);//异步写
co_await async_write
(socket, boost::asio::buffer(data, n), use_awaitable);

2.客户端

客户端我们还是用以前的简易的版本,发送 hello world 进行测试,不考虑其他的问题

#include <iostream>
#include<boost/asio.hpp>using namespace std;
using namespace boost::asio::ip;
const int MAX_LENGTH = 1024;int main()
{try{boost::asio::io_context ioc;tcp::endpoint remote_ep(boost::asio::ip::make_address("127.0.0.1"), 10086);tcp::socket sock(ioc);boost::system::error_code error = boost::asio::error::host_not_found;sock.connect(remote_ep, error);if (error){cout << "connect failed, code is" << error.value() <<"  error msg is "<<error.what() << endl;return 0;}cout << "Enter Message:" << endl;char request[MAX_LENGTH];cin.getline(request, MAX_LENGTH);size_t request_len = strlen(request);boost::asio::write(sock, boost::asio::buffer(request, request_len));char reply[MAX_LENGTH];size_t reply_len = boost::asio::read(sock, boost::asio::buffer(reply, request_len));cout << "reply is" << string(reply,reply_len) << endl;getchar();}catch (std::exception& e){std::cout << "main exception is " << e.what() << std::endl;}return 0;
}

四、测试

客户端成功与服务器进行通信
在这里插入图片描述

总结

学习了协程了相关概念,以及如何利用协程来搭建一个简易的异步服务器的小demo

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

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

相关文章

【Java21】在spring boot中使用虚拟线程

文章目录 0.环境说明1.原理解析2.spring boot的方案3.注意事项&#xff08;施工中&#xff0c;欢迎补充&#xff09; 前置知识 虚拟线程VT&#xff08;Virtual Thread&#xff09; 0.环境说明 用于验证的版本&#xff1a; spring boot: 3.3.3jdk: OpenJDK 21.0.5 spring boot…

利器:NPM和YARN及其他

文章目录**1. 安装 Yarn&#xff08;推荐方法&#xff09;****2. 验证安装****3. 常见问题及解决方法****① 权限不足&#xff08;Error: EPERM&#xff09;****② 网络问题&#xff08;连接超时或下载失败&#xff09;****③ 环境变量未正确配置****4. 替代安装方法&#xff0…

跨平台直播美颜SDK集成实录:Android/iOS如何适配贴纸功能

众所周知&#xff0c;直播平台与短视频平台的贴纸功能不仅是用户表达个性的方式&#xff0c;更是平台提高用户粘性和互动转化的法宝。 可问题来了&#xff1a;如何让一个贴纸功能&#xff0c;在Android和iOS两大平台上表现一致、运行流畅、加载稳定&#xff1f;这背后&#xff…

JavaWeb(苍穹外卖)--学习笔记04(前端:HTML,CSS,JavaScript)

前言 本片文章是学习B站黑马程序员苍穹外卖的学习笔记。因为最近期末周&#xff0c;一直在应付考试所以就学的很少&#xff0c;恰好视频中在讲Nginx反向代理和负载均衡&#xff08;写着对前端的内容做一个复习&#xff09; 概述&#xff1a; 1.web前端主要由三部分组成&…

智能学号抽取系统 V5.4.3.2 —— Vue.js 实现的多功能课堂随机抽签工具

智能学号抽取系统 V5.4.3.2 —— Vue.js 实现的多功能课堂随机抽签工具 在教学或会议场景中&#xff0c;我们经常需要随机抽取一个或多个学号/编号来决定发言者、答题者或者参与者。为了提高效率和公平性&#xff0c;我们可以使用一些智能化的小工具来实现这一过程。 今天介绍…

从0开始学习R语言--Day39--Spearman 秩相关

在非参数统计中&#xff0c;不看数据的实际数值&#xff0c;单纯比较两组变量的值的排名是通用的基本方法&#xff0c;但在客观数据中&#xff0c;很多变量的关系都是非线性的&#xff0c;其他的方法不是对样本数据的大小和线性有要求&#xff0c;就是只能对比数据的差异性&…

WSL - Linux 安装 Anaconda3-2025.06-0 详细教程 [WSL 分发版均适用]

一、检查系统状态 安装前先确认 WSL - Linxu 已正常启动&#xff08;比如 Ubuntu&#xff09;&#xff0c;网络连接稳定&#xff0c;并且系统磁盘有足够空间&#xff0c;一般建议预留至少 5GB 以上的可用空间&#xff0c;避免因空间不足导致安装失败。 二、下载安装包 Anacond…

热血三国建筑攻略表格

<!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>热血三国建筑攻略表格</title><style>…

SpringBoot+MySQL医院挂号系统源码

概述 基于SpringBootMySQL开发的医院挂号系统完整源码&#xff0c;该系统功能完善&#xff0c;包含从患者挂号到医生管理的全流程解决方案&#xff0c;采用主流技术栈开发&#xff0c;代码规范易于二次开发。 主要内容 系统包含完整的前后台功能模块&#xff1a; ​​前台功…

Linux系统之MySQL数据库基础

目录 一、概述 数据库概念 数据库的类型 关系型数据库模型 关系数据库相关概念 二、安装 1、mariadb安装 2、mysql安装 3、启动并开机自启 4、本地连接&#xff08;本地登录&#xff09; 三、mysqld数据库配置与命令 yum安装后生成的目录 mysqld服务器的启动脚本 …

MySQL--InnoDB存储引擎--页结构

目录 一、页的大小 二、页的分类 三、页头和页尾 3.1 页头--File Header 3.2 页尾--File Trailer 3.3 LSN 四、数据行 五、页中数据的查询 六、事务和索引在页中的记录 一、页的大小 前面介绍了每个数据页默认大小为16KB&#xff0c;是操作系统“数据块” 4KB 的整数倍…

卡车检测数据集-700张图片交通运输管理 智能监控系统 道路安全监测

跌倒检测数据集-4500张图片&#x1f4e6; 已发布目标检测数据集合集&#xff08;持续更新&#xff09;&#x1f69b; Deteccin de carpa 2 Computer Vision Project&#x1f4cc; 数据集概览包含类别&#x1f3af; 应用场景&#x1f5bc; 数据样本展示&#x1f527; 使用建议&a…

Python爬虫实战:研究pangu库相关技术

1. 引言 1.1 研究背景与意义 在数字化信息传播时代,中文文本排版质量直接影响信息传达效果。规范的排版要求中文与西文、数字间保持合理空格间距,但人工处理不仅效率低,且易出现一致性问题。随着互联网中文内容爆发式增长,传统人工排版已无法满足需求。Python 作为高效的…

day48-考试系统项目集群部署

1. ✅考试系统项目集群架构图负载均衡说明7层负载通过nginx对http请求进行转发&#xff08;uri,ua,类型&#xff09;4层负载对端口负载均衡&#xff08;后端&#xff09;2. &#x1f4dd;环境准备角色主机ip负载均衡lb01/lb02172.16.1.5/172.16.1.6前端web集群web01/web02172.1…

Redis+Caffeine双层缓存策略对比与实践指南

RedisCaffeine双层缓存策略对比与实践指南在高并发场景下&#xff0c;缓存是提升系统性能和并发处理能力的关键手段。常见的缓存方案包括远程缓存&#xff08;如Redis&#xff09;和本地缓存&#xff08;如Caffeine&#xff09;。单层缓存各有优劣&#xff0c;结合两者优势的双…

FastAPI+React19 ERP系统实战 第02期

一、搭建环境 1.1 创建Python虚拟环境 切换Python版本: pyenv local 3.12创建虚拟环境: python -m venv venv激活虚拟环境: venv\Scripts\activate1.2 安装FastAPI项目依赖 requirements.txt fastapi==0.109.1

百度AI文心大模型4.5系列开源模型评测,从安装部署到应用体验

2025年6月30日&#xff0c;百度突然宣布&#xff0c;将旗下最新的大语言模型文心大模型4.5&#xff08;ERNIE 4.5&#xff09;全系列开源&#xff0c;震动整个AI行业。百度在GitCode平台上开源了文心大模型4.5系列&#xff0c;包括ERNIE-4.5-VL-424B-A47B-Base-PT等多个型号。此…

windows安装maven环境

在maven官网下载安装包 https://maven.apache.org/download.cgi 下载完成后安装maven&#xff0c;一般下载编辑好的 创建个maven目录解压出来即可 配置环境变量 根据刚刚的安装路径&#xff0c;新建一个命名为MAVEN_HOME的系统变量 新建完成点开系统变量的Path项&#xff0c;…

MySQL(117)何进行数据库安全加密?

数据库安全加密是保护敏感数据免受未授权访问的重要手段。以下是一个详细深入的步骤&#xff0c;介绍如何进行数据库安全加密&#xff0c;包括数据传输加密和数据存储加密。 一. 数据传输加密 确保数据在传输过程中被加密&#xff0c;以防止中间人攻击。我们以MySQL为例&#x…

工程化实践——标准化Eslint、PrettierTS

前端工程化中的标准化工具&#xff08;如Prettier、ESLint、Husky等&#xff09;虽然大幅提升了开发效率和代码质量&#xff0c;但在实际使用中也存在一些限制和挑战。以下从工具特性、团队协作、开发体验等维度详细分析常见限制&#xff0c;并以Prettier为核心举例说明&#x…