0

0

C++如何利用协程改写复杂的异步握手协议流程?(网络协议重构)

尼克

尼克

发布时间:2026-03-14 13:45:48

|

802人浏览过

|

来源于php中文网

原创

协程中不能直接co_await裸socket,需封装为awaitable对象;推荐用boost::asio::use_awaitable;TLS握手需注意资源生命周期、串行操作保护及细粒度控制。

c++如何利用协程改写复杂的异步握手协议流程?(网络协议重构)

协程改写异步握手时,co_await 不能直接套在裸 socket 上

socket 是阻塞或非阻塞的系统资源,不满足 awaitable 要求。强行 co_await 一个 recv() 调用会编译失败,报错类似 no operator "co_await" matches these operands

必须封装成可等待对象:要么用第三方库(如 libunifexcppcoro)提供的 async_read / async_write,要么自己实现 await_suspend/await_resume —— 但后者需配合 epoll/kqueue 或 IOCP,不是加个 co_await 就完事。

  • 推荐路径:用 boost::asio::use_awaitable,它已封装好 async_handshakeasync_read 等成员函数,返回原生 awaitable
  • 别踩坑:别试图对 std::threadstd::mutexco_await —— 它们不参与调度,会卡死或 UB
  • 注意:boost::asio::io_context 必须在协程挂起前已启动(run()run_one()),否则协程永远不恢复

TLS 握手阶段拆分协程,async_handshakehandshake_type 参数不能忽略

典型 TLS 握手包含 client_hello → server_hello → cert → finished 多轮交互,但 boost::asio::ssl::stream::async_handshake 默认只暴露一个顶层 awaitable。如果协议要求在收到 server_cert 后插入自定义校验逻辑(比如查本地策略库),就不能全交给它自动完成。

此时得降级到更细粒度操作:async_handshake 支持传入 boost::asio::ssl::stream_base::serverclient,但关键在后续手动调用 async_readasync_write 配合 ssl::stream::next_layer()

立即学习C++免费学习笔记(深入)”;

Napkin AI
Napkin AI

Napkin AI 可以将您的文本转换为图表、流程图、信息图、思维导图视觉效果,以便快速有效地分享您的想法。

下载
  • 常见错误:把 async_handshake(client) 当成原子操作,结果证书校验失败后无法中断流程,只能等超时
  • 正确做法:用 ssl::stream::async_handshake(boost::asio::ssl::stream_base::client, token) 启动,再通过 ssl::stream::lowest_layer().async_read_some() 拦截原始 TLS 记录,解析 Certificate 消息
  • 性能影响:手动解析 TLS 记录会绕过 asio ssl 内部缓冲,增加拷贝和 CPU 开销;若无强定制需求,优先用 set_verify_callback 注入校验逻辑

多个握手步骤串行 await,别让 co_await 变成隐式同步点

看似自然的写法:co_await step1(); co_await step2(); co_await step3();,在握手流程里容易误以为“并发安全”,其实每个 co_await 都会让出执行权,但底层 socket 仍是单线程共享的 —— 如果多个协程同时读写同一个 ssl::stream,会触发未定义行为(asio 文档明确要求:one stream, one operation at a time)。

  • 典型现象:随机出现 SSL_ERROR_SYSCALLasio.ssl.stream: bad length 错误,尤其在高并发重连场景
  • 必须加保护:用 boost::asio::strand 包裹整个握手协程,或用 awaitable_mutex(cppcoro)限制同一时刻仅一个协程访问该连接
  • 别省事:不要用 std::shared_mutex 手动锁 —— 协程挂起期间锁不会释放,会导致死锁

协程栈与 TLS 上下文生命周期必须对齐

握手过程中创建的临时对象(如证书解析结果、密钥派生中间值)如果放在协程栈上,而协程被挂起后栈帧销毁,resume 时访问就是野指针。更隐蔽的是 ssl::context 实例若被提前析构,后续 async_handshake 会 crash 在 SSL_CTX_free 相关断言上。

这不是语法问题,是 RAII 和协程生命周期的错位。

  • 容易忽略:把 ssl::context 声明在协程函数内部,而不是传入或由连接对象持有
  • 正确方式:所有 SSL 相关资源(ssl::contextssl::stream、证书缓存)必须比协程活得久;建议绑定到 connection 对象的成员变量
  • 调试提示:启用 asio 的 ASIO_ENABLE_HANDLER_TRACKING,能快速定位 resume 时访问已释放内存的协程栈

真正麻烦的从来不是怎么写 co_await,而是搞清谁 owns 什么、谁负责 cleanup、以及挂起那一刻哪些指针还有效。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
登录token无效
登录token无效

登录token无效解决方法:1、检查token的有效期限,如果token已经过期,需要重新获取一个新的token;2、检查token的签名,如果签名不正确,需要重新获取一个新的token;3、检查密钥的正确性,如果密钥不正确,需要重新获取一个新的token;4、使用HTTPS协议传输token,建议使用HTTPS协议进行传输 ;5、使用双因素认证,双因素认证可以提高账户的安全性。

6632

2023.09.14

登录token无效怎么办
登录token无效怎么办

登录token无效的解决办法有检查Token是否过期、检查Token是否正确、检查Token是否被篡改、检查Token是否与用户匹配、清除缓存或Cookie、检查网络连接和服务器状态、重新登录或请求新的Token、联系技术支持或开发人员等。本专题为大家提供token相关的文章、下载、课程内容,供大家免费下载体验。

843

2023.09.14

token怎么获取
token怎么获取

获取token值的方法:1、小程序调用“wx.login()”获取 临时登录凭证code,并回传到开发者服务器;2、开发者服务器以code换取,用户唯一标识openid和会话密钥“session_key”。想了解更详细的内容,可以阅读本专题下面的文章。

1092

2023.12.21

token什么意思
token什么意思

token是一种用于表示用户权限、记录交易信息、支付虚拟货币的数字货币。可以用来在特定的网络上进行交易,用来购买或出售特定的虚拟货币,也可以用来支付特定的服务费用。想了解更多token什么意思的相关内容可以访问本专题下面的文章。

2201

2024.03.01

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

447

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

606

2023.08.10

length函数用法
length函数用法

length函数用于返回指定字符串的字符数或字节数。可以用于计算字符串的长度,以便在查询和处理字符串数据时进行操作和判断。 需要注意的是length函数计算的是字符串的字符数,而不是字节数。对于多字节字符集,一个字符可能由多个字节组成。因此,length函数在计算字符串长度时会将多字节字符作为一个字符来计算。更多关于length函数的用法,大家可以阅读本专题下面的文章。

954

2023.09.19

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

766

2023.08.10

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

26

2026.03.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 11.3万人学习

C 教程
C 教程

共75课时 | 5.4万人学习

C++教程
C++教程

共115课时 | 21.7万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号