首页 > 其他分享 >boost asio 协程-实现echo server

boost asio 协程-实现echo server

时间:2023-08-08 14:22:38浏览次数:32  
标签:asio co read await server async 协程

ASIO服务器模式:协程

最新版asio提供了协程来实现使用同步代码进行异步编程,这比异步调用写法方便太多。本文将以ASIO的示例代码为基础作讲解,代码见:http://think-async.com/Asio/boost_asio_1_13_0/doc/html/boost_asio/example/cpp17/coroutines_ts/echo_server.cpp

一、什么是协程

所谓协程(Coroutines),可以理解为线程的线程,但是协程之间的等待与同步不需要操作系统介入,因此开销远远小于线程。通过协程,可以按照同步的形式书写代码,完成异步逻辑。

使用协程最重要的关键字是co_await。这个可以理解为一个运算符,跟new差不多

std::size_t n = co_await socket.async_read_some(boost::asio::buffer(data), use_awaitable);

从语义上来说,一旦使用了co_await,则代码执行到这里直接挂起(但是函数并没有return),然后正常执行外层代码。挂起和返回的本质区别是返回则栈的内容都销毁,挂起则全部保留。
一旦co_await右侧的操作完成后,直接回到这段代码,再执行下面的部分。(之所以可能继续执行是因为栈的内容全部保留下来了)
也就是说相当于完成了一个异步操作,然而代码形式却是同步的
特别注意多个协程都是在一个线程里运行的,所以不需要考虑锁。

下面来看看协程框架的写法。

二、入口

main()函数里重点就是圈红的两段,一头一尾是ASIO标准写法。
第一段跟协程没什么关系,其作用是注册一个信号机制,用于收到操作系统信号后执行一段代码。比如SIGTERM在linux上对应kill命令,那么这样注册后当你在终端kill这个程序时,程序会先执行io_context.stop(); 然后再交给操作系统做退出。所以这段的意义就是clean exit。

下面的co_spawn才是协程重点。listener是一个协程,从语义上说这行代码就是以分离方式启动listener协程,没了。
detach的写法跟std::thread很像,估计是统一style。detach还带有nothrow的含义,即该协程抛出异常的话主线程会无视

三、监听sock连接

for循环之前是固定写法。
之前说过协程之间的挂起和切换不是操作系统而是程序自己负责的,那么总需要有一个协调人员,就是这个executor,另外这个对象也能够当作io_context使用。
(this_coro跟this_thread写法相近,也是统一style)

for之内有两部分。
co_await acceptor.async_accept写法表示异步等待客户端sock连接。执行到这一句如果已经有连接那就取出,如果没有则不阻塞而是挂起,然后执行外部代码,等到有连接了会自动切回来然后继续执行。

后面那一大块看起来挺可怕,实际上就是一个lambda。对于新协程需要用到的变量,使用lambda+捕获的形式传进去,或者使用std::bind将变量加入同时保持调用形式一致。
比如可以改成一行代码co_spawn(executor, std::bind(CoSession, pSock), detached);
但是注意传递参数的合理性,建议用shared_ptr

四、处理客户端业务的协程

执行到co_await async_read_some后直接挂起。一旦有信息传过来后,则切回这里继续执行下面的语句。本质上是一个异步操作,这就是使用了同步的代码形式达成了异步逻辑的典型。

co_await async_write一般而言不必要,使用同步的方式send即可。

协程里不能使用return,而是要用co_return。要让协程返回参数,则awaitable里的T指定类型。

再补充几句asio的读数据方法。

asio提供了read_until方便处理line-based protocol,写法如下:
asio::streambuf sbuf;
auto nSize = co_await asio::async_read_until(sock, sbuf, ‘\n’, use_awaitable);
asio::streambuf::const_buffers_type bufs = sbuf.data();
std::string strInfo(asio::buffers_begin(bufs), asio::buffers_begin(bufs) + nSize – 1);
sbuf.consume(nSize)
但是需要注意的是,只要有一方shutdown,另一方就自动也shutdown了。因此如果对方正在read但是这边shutdown了,则对方会抛出异常:sock not connected。已经达到的信息也读不出来。因此对于发送方而言,此时要做的应该是等待对方shutdown。
可以使用等待操作的方法实现,写法co_await sock.async_wait(tcp::socket::wait_read, use_awaitable);
该函数不会抛出异常,如果有信息抵达就返回,或者socket断了无法继续读取也直接返回
另外还要注意,read_until会读取超过delimiter,多出来的内容只在下一次read_until可见,换句话说如果使用了read_until那么接下来都应该使用这个,不要跟其他read混用
如果要读delimiter,那么客户端上可以考虑逐字节read然后判定读出来的是否是\n

但是个人喜欢用另一种读取数据的方式。
co_await sock.async_receive(boost::asio::buffer(data, 1), tcp::socket::message_peek, use_awaitable);
只判定出有数据到达sock,然后自己调用sock.availble()得到有多少数据可用,然后直接read读取到std::string里再进一步操作。

五、总结

使用asio协程的套路就是:main里启动accept协程 -> accept协程无限循环co_await接受客户端连接并启动处理客户端的协程 -> 客户端协程co_await接收传过来的数据再进一步处理

有了协程处理并发就是小菜一碟。

https://dins.site/coding-lib-socket-asio-server-coroutine-chs/

标签:asio,co,read,await,server,async,协程
From: https://www.cnblogs.com/bwbfight/p/17614081.html

相关文章

  • hadoop组件---数据仓库(五)---通过JDBC连接hive的thrift或者hiveserver2
    我们在上一篇文章中已经学习了Hive的常用命令,但是如果使用其他的语言如何跟Hive进行交互呢。Thrift简介Hive拥有HiveServer(Thrift)或者Hiveserver2组件,提供了JDBC驱动服务,使得我们可以用Java代码或者Python来连接Hive并进行一些关系型数据库的sql语句查询等操作。HiveServer或者Hi......
  • Android协程带你飞越传统异步枷锁
    引言在Android开发中,处理异步任务一直是一项挑战。以往的回调和线程管理方式复杂繁琐,使得代码难以维护和阅读。Jetpack引入的Coroutine(协程)成为了异步编程的新标杆。本文将深入探讨AndroidJetpackCoroutine的使用、原理以及高级用法,助您在异步编程的路上游刃有余。什么是Coroutin......
  • Pentaho Report Design发布报表到Pentaho Server
    我们在前面两章分别学习了windows安装PentahoReportDesignPentahoReportDesign设计柱状图windows安装PentahoServer本章来学习PentahoReportDesign发布报表到PentahoServer。PentahoReportDesign准备报表使用PentahoReportDesign设计好报表之后需要呈现给其他人看时,......
  • Siemens SERVER 2016中安装WINCC 7.5 SP1
     一、查询WINCC兼容性列表,得知WINCC7.5可以在SERVER2016中安装,且与SIMATICNETV16兼容:二、了解了系统及软件的兼容性之后,开始准备操作系统及软件。1.安装VMware虚拟机,内容略过……2.部署英文版WindowsServer2016系统;3.安装中文语言包;4.安装消息队列、IIS、NetFramew......
  • SqlServer时间格式化
       sqlserver日期格式化中文-年月日selectconvert(varchar,datepart(yy,getdate()))+N'年'+convert(varchar,datepart(mm,getdate()))+N'月'+convert(varchar,datepart(dd,getdate()))+N'日',cast(datepart(yy,getdate())asvarchar)+N'年......
  • 调用Geoserver发布的图层中文字段显示乱码
    通过OL使用WFS服务,调用发布的图层字段,中文字段显示为乱码  有几种原因:①指定打印的字符格式在代码中指定输出的编码格式,例如使用console.log('@@xxx',xzq.toString('utf-8'))来指定输出为UTF-8编码格式。②添加meta如果是在网页中输出乱码,可以在HTML的<head>标......
  • 服务器数据恢复-断电导致XenServer虚拟磁盘文件丢失的数据恢复案例
    服务器数据恢复环境:某单位一台Dell服务器上使用RAID卡搭建了一组由4盘RAID10。服务器安装的XenServer虚拟化操作系统,虚拟机采用的WindowsServer操作系统。共系统盘和数据盘两个虚拟机磁盘,上层部署的是Web服务器(ASP+SQLServer架构)。服务器故障&分析:由于服务器突然断电,服务器......
  • Windows server 2003怎么安装iisWindows server 2003安装IIS教程
    Windows2008系统服务器安装IIS之前已经分享过了,和Windows2003完全不同,今天我将详细地和你分享Windowsserver2003卸载和安装IIS的步骤方法,希望可以帮助到你~1、首先进入服务器,确定下服务器是否有安装IIS,有安装IIS,需要重装的,可以先将IIS卸载。2、卸载比安装更简单些,点击开始——......
  • MySQL问题记录Can't connect to MySQL server on 'localhost' (10061)解决方法
    登录MySQL提示Can'tconnecttoMySQLserveron'localhost'(10061)进入安装目录bin目录,执行mysqld--install,启动MySQL点击查看代码cdD:\soft\MySQL\MySQLServer5.7\binmysqld--installnetstartmysql提示启动失败最后执行mysqld--initialize--user=root--......
  • $_SERVER 全局变量内容详解
    echo"<h1>服务器</h1>";//**********************  服务器  *********************echo $_SERVER['SERVER_NAME']."<br />";     //服务器的名称echo $_SERVER['SERVER_ADDR']."<br />";     //服务器的ipecho ......