2308C++简单异步改造网络库

简单异步可以轻松改造同步网络库从而获得大幅性能提升,用它改造异步回调网络库可以让我们以同步方式写代码,让代码更简洁,可读性更好,还能避免回调地狱的问题.
本文通过两个例子分别来介绍如何用简单异步改造基于asio的同步网络库和异步回调网络库.

示例依赖了独立版的asio(commitid:f70f65ae54351c209c3a24704624144bfe8e70a3),它是headeronly的直接包含头文件即可.

改造同步网络库

以一个同步的echo server/client为例,先来看看echoserver:

空 开始服务器(异网::io环境&io环境,正 短 端口){
    传控::受者 a(io环境,传控::端点(传控::v4(),端口));(;;){[错误,套接字]=接受(a);
        会话(::移动(套接字));
    }
}

echoserver启动时先监听端口,然后同步accept,当有新连接到来时在会话中处理网络读写事件.

<型名 异网缓冲>::<异网::错误码,大小型>读些(异网::ip::传控::套接字&套接字, 异网缓冲&&缓冲){
    异网::错误码 错误;
    大小型 长度=套接字.读些(::前向<异网缓冲>(缓冲),错误);
    中 标::造双(错误,长度);
}

空 会话(传控::套接字 套接字){(;;){
        常 大小型 最大长度=1024;
        符 数据[最大长度];[错误,长度]=读些(套接字,数据,最大长度);(套接字,数据,长度);
    }
}

循环中先同步读数据,再把读到的数据发送到对端.

同步的echo client:

空 开始(异网::io环境&io环境,::串 主机,::串 端口){[ec,套接字]=连接(io环境,主机,端口);
    常 整 最大长度=1024;
    符 写缓冲[最大长度]={"你好 简单异步"};
    符 读缓冲[最大长度];
    常 整 数=10000;(整 i=0;i<;++i){(套接字,写缓冲,最大长度);[错误,回复长度]=读些(套接字,读缓冲,最大长度);
        //处理数据.
    }
}

同步的echoclient也是类似的过程,先连接,再循环发送数据和读数据.整个逻辑都是同步的,代码简单易懂.

当然,因为整个过程都是同步等待的,所以无法并发的处理网IO事件,性能是比较差的.如果用简单异步来改造这个同步的网络库则可以大幅提升网络库的性能(参考benchmark),而且需要修改的代码很少,这就是简单异步的威力.

简单异步改造同步echoserver

简单异步::协程::<>会话(传控::套接字 套接字){(;;){
        常 大小型 最大长度=1024;
        符 数据[最大长度];[错误,长度]=
            协待 异步读些(套接字,异网::缓冲(数据,最大长度));
        协待 异步写(套接字,异网::缓冲(数据,长度));
    }::错误码 ec;
    套接字.关闭(异网::ip::传控::套接字::都关闭,ec);
    套接字.关闭(ec);::输出<<"完成回声消息,总:"<<消息索引-1<<".\n";
}

简单异步::协程::<>开始服务器(异网::io环境&io环境, 正 短 端口, 简单异步::执行器*E){
    传控::受者 a(io环境,传控::端点(传控::v4(),端口));(;;){[错误,套接字]=协待 异步接受(a);
        会话(::移动(套接字)).通过(E).开始([](&&){});
    }
}

可以看到用简单异步改造的start_server会话相比,返回类型变成了Lazy,同步接口变成了co_await async_xxx,原有的同步逻辑没有任何变化,这个微小的改动即可让我们把同步网络库改成异步协程网络库从而让性能得到巨大的提升.

更多的代码细节请看demo_exampleechoserver/client的例子.

改造异步回调网络库

对于一些已有的异步回调网络库,也可以用简单异步来消除回调,从而让我们可以用同步方式去使用异步接口,让代码变得更简洁易懂.

asio异步httpserver为例:

空 连接::干读()
{
  动 本(从本共享());
  套接字_.异步读些(异网::缓冲(缓冲_),
      [,](::错误码 ec,::大小型 传输字节)
      {(!ec)
        {
          请求解析器::结果类型 结果;::绑定(结果,::忽略)=请求解析器_.解析(请求_,缓冲_.数据(),缓冲_.数据()+传输字节);(结果==请求解析器::)
          {
            请求处理器_.请求处理(请求_,回复_);
            干写();
          }
          异 如(结果==请求解析器::)
          {
            回复_=回复::回复股票(回复::坏请求);
            干写();
          }{
            干读();
          }
        }
        异 如(ec!=异网::错误::中止操作)
        {
          连接管_.停止(从本共享());
        }
      });
}

空 连接::干写()
{
  动 本(从本共享());
  异网::异步写(套接字_,回复_.到缓冲(),
      [,](::错误码 ec,::大小型)
      {(!ec)
        {
          异网::错误码 忽略误码;
          套接字_.关闭(异网::ip::传控::套接字::都关闭, 忽略误码);
        }(ec!=异网::错误::中止操作)
        {
          连接管_.停止(从本共享());
        }
      });
}

可以看到基于回调的异步网络库的代码,比同步网络库复杂很多,如在异步读的回调中如果没有读到完整数据需要递归调用do_read,如果读到完整数据之后才能在回调中调用异步写接口.同时,还要注意将shared_from_this()得到的std::shared_ptr传入到异步接口的回调函数中以保证安全的回调.总之,异步回调代码的编写难度较大,可读性也较差,如果用简单异步改造则可以较好的解决这些问题,性能也不会降低.

简单异步改造异步httpserver

简单异步::协程::<>干读(){
    动 本(从本共享());(;;){[错误,传输字节]=
            协待 异步读些(套接字_,异网::缓冲(读缓冲_));(错误){;}

        请求解析器::结果类型 结果;::绑定(结果,::忽略)=解析器_.解析(
            请求_,读缓冲_,读缓冲_+传输字节);(结果==请求解析器::){
            请求处理(请求_,响应_);
            协待 异步写(套接字_,响应_.到缓冲());
        }异 如(结果==请求解析器::){
            响应_=构建响应(状态类型::坏请求);
            协待 异步写(套接字_,响应_.到缓冲());;
        }
    }
}

简单异步改造之后的httpserver完全消除了回调函数,完全可按同步方式写异步代码,简洁易懂.
测试结果,提高2倍速度!

你可能感兴趣的:(c++,cpp,c++,网络)