eos源码赏析(四):基于boost::asio的httpserver架构

eos源码赏析(四):基于boost::asio的httpserver架构_第1张图片

前言

    eos代码更新很快,4初已经升级到3.0版本,随着版本的更迭,在各个操作系统的编译、节点的运行都越来越集成化,不需要自己再一步步的下载依赖,如果感兴趣可以直接按照官方wiki进行编译官方wiki地址:https://github.com/EOSIO/eos/wiki

    一般不会出现什么错误,当然如果出现编译、测试节点运行出错的情况可以添加我个人微信(见本文最下方),我会尝试着去解答一下。有鉴于eos代码更新的速度我们现在也大可不必关注一些具体的实现,而是作出一些调整,通过更深层次的探讨去观察整个eos代码的架构。

    自代码更新至3.0版本之后,eosioc也变成了cleos,通过代码的注释,我们可以看出cleos是一个基于命令行对eos进行一些简单的交易、获取nodeos的状态等功能如果想要使用这个命令行去进行相应的操作,需要本地有一个nodeos在运行,且chain_api_plugin需要加载成功。

 

eos源码赏析(四):基于boost::asio的httpserver架构_第2张图片

1 cleos代码功能简介

    cleos很多参数可以配置选用,具体每一个点官方wiki也都有介绍,在下一篇文章中我们也将继续进行详细分析。这些功能的实现都需要通过http请求来完成,今天我们来谈谈一些细节的东西,如cleos是如何搭建一个httpserver的。

Boost::asio简介

    首先我们来看cleosmain.cpp中,不管是交易还是获取钱包、获取账户的状态等功能都会调用一个函数do_http_call,在55eos-master中最新的更新记录中,Daniel Larimer也将cleos中的::call全部替换成了do_http_call,那么do_http_call到底是一个什么样的函数?他实现了什么功能?是如何实现的?让我们对他一探究竟。跳转到do_http_call这个函数,我们可以看到其实这个函数是基于boost::asio实现的一个httpserver,供cleos的http通信使用.

Boost::asio是一种跨平台的主要用于网络和其他一些底层输入/输出的C++库。Boost::asio在网络通信、COM串行端口和文件上成功的抽象了输入输出的概念。我们可以基于这些进行同步或者异步的网络编程。作为一个跨平台的库,Boost::asio可以在大多数操作系统上使用,且能够同时支持数千个并发的连接。其网络部分的灵感来源于socket协议,提供了一套可以支持TCPUDPIMCP协议的API,而且如果有需要的话,可以对其进行扩展。Boost::Asio基本框架如1所示

eos源码赏析(四):基于boost::asio的httpserver架构_第3张图片

2 Boost::Asio基本框架

    使用者启动一个异步操作,同时创建一个异步回调的对象。然后异步操作被交给了异步操作执行者,通过异步操作执行者来执行异步操作,当异步操作完成之后,将其插入完成事件队列。前摄器驱动异步事件分发器从刚刚异步操作完成插入的完成事件队列中获取事件,这是一个阻塞的过程,一旦获取到完成事件,就会从事件中找出关联的回调对象,并执行回调。

基于AsioHttpServer的实现

每一个Asio服务的实现都需要至少一个io_service类,io_service只有三个成员变量,简单意味着强大,也表明asio已经将功能结构划分的清晰明了。如图3所示

eos源码赏析(四):基于boost::asio的httpserver架构_第4张图片

3 io_service成员变量示例

Asio提供了诸多服务,但是上层服务不会直接使用这些服务,这些服务是通过句柄对外暴露其功能,而句柄被功能对象封装,然后提供给上层应用使用。因此前面的前摄器模式可以简单的添加IO对象如4所示

eos源码赏析(四):基于boost::asio的httpserver架构_第5张图片

4 添加IO对象之后的asio结构图

acceptor为例,通过源码可以发现他是basic_socket_acceptorTCP模板参数下的一个实例。Asio大量采用这种技术,所有的io功能类,都是单个basic模板类的实例化。这些实例化的的类,分别负责一些具体的事物,如acceptor可以作为一个服务器进行侦听,提供了诸如bind()listen()等接口。再如basic_socket是对socket IO操作的封装,提供了receive(),async_recive()read_some()async_readsome()write_some()async_write_some()等接口。如5所示

eos源码赏析(四):基于boost::asio的httpserver架构_第6张图片

5 asioio对象结构图

基于asioHttpServer的基本框架如6所示

eos源码赏析(四):基于boost::asio的httpserver架构_第7张图片

6 基于asio的HttpServer框架

io_service::run()io_service最重要的函数。主线程启动run()之后,将其交给impl_.run().通过源码可以发现,在windows操作系统下,如果没有禁用IOCPasio就会采用win_iocp_io_service来完成io_service::run()的功能。win_iocp_io_servicewindows操作系统下boost::asio实现的核心,他是对windows环境下IOCP(完成端口IO)模型的封装。io_service的构造函数会调用win_iocp_io_service::init(),这个过程会创建一个完成端口句柄,而当io_service::run()的时候会调用win_iocp_io_service::do_one(),do_one()会做一次完成端口状态查询并处理,run()就会一直重复调用do_one()完成I/O操作。如图7所示

eos源码赏析(四):基于boost::asio的httpserver架构_第8张图片

7 impl_windows下的结构图

Server类中主要包含有用于执行异步操作的io_service_,用于侦听连接请求的acceptor_,用于存放所有活动连接的connection_manager_,用来存放新建连接的new_connection_以及处理将要到来请求的句柄request_handler_。通过源码可以发现,用来存放新建连接的connection_ptr其实是一个shared_ptr类型,他是实现连接自动释放的关键。

connection类内存管理机制:当接收到客户端的连接请求之后,使用一个shared_ptr对象持有一个新建的连接对象,shared_ptr转而持有其他对象时,将对此连接对象的引用计数减一,而connection类中的异步处理函数中传递的this指针都是通过share_from_this获取的,这个传递的this指针也是被shared_ptr进行管理的。处理完毕后引用计数自动减一,当与这个连接的相关操作都执行完毕以后,连接对象的引用计数为0,自动释放,由此实现了每个客户端连接创建一个连接对象,连接对象处理完请求之后释放自己。如8所示

eos源码赏析(四):基于boost::asio的httpserver架构_第9张图片

8 connection启动过程

handle_read中使用request_parser_.parse拉解析来自客户端的数据,并将解析结果放入到buffer中。此时定义了一个三状态result。当解析数据成功的时候,则进行数据的处理并生成返回的内容。当解析数据失败的时候,则向客户端发送请求失败的内容,还有一种未知状态,则继续接收来自客户端的请求。下面以解析成功为例,如图9所示

eos源码赏析(四):基于boost::asio的httpserver架构_第10张图片

9 来自客户端内容的解析

当使用parse解析来自客户端的数据正常时,则将数据存入buffer中,并进行返回结果的处理。本例将使用者在QT配置的界面中写入的数据为返回内容,以json串的形式回传给客户端。向客户端异步发送相应的数据之后,调用handle_write停止该socket的发送和接收,但是并未释放这个socket对象,结束了这一次http的请求,并从连接池中将这个连接清除掉,回收这个连接对象的内存空间。如10所示

 

eos源码赏析(四):基于boost::asio的httpserver架构_第11张图片

10 handle_write操作示例

Parse是对来自客户端请求内容的解析,返回请求内容、起始迭代器、截止迭代器。Consume分析传入的char类型的参数,根据协议及当前状态解析这个字符,将char字符加入到request的结构体成员中。如果一个request没有解析完成则返回未知状态,继续进行下一个字符的解析。Connection类的构造函数中会构造一个连接池对象,创建好连接之后将连接自动放入缓冲池中管理。如1112所示:

eos源码赏析(四):基于boost::asio的httpserver架构_第12张图片

11 parse解析类的实现

eos源码赏析(四):基于boost::asio的httpserver架构_第13张图片

12 连接池的实现

Server类中创建connection实例需要从线程池中通过get_io_service来获io_service对象。异步操作由哪个线程执行与io_service对象有关。因此要想实现线程池,首先要在线程池对象中创建多个io_service对象同时还要创建多个线程对象,这样每个io_service调用run即可实现异步操作均匀的将多个io_service对象分配给多个线程执行了。如1314所示

eos源码赏析(四):基于boost::asio的httpserver架构_第14张图片

13 多线程io_service的HttpServer的实现

eos源码赏析(四):基于boost::asio的httpserver架构_第15张图片

14 线程池get_io_service示例

    由于本人是做windowsqt开发的,因此基于qt界面库和boost::asio实现了一个测试小工具,支持http post(暂不支持https协议get请求,有需要可以继续补充方式请求解析处理、并给出一定的返回简单的界面如下(丑了点,但是可以用),有需要源码的可以加我个人微信

eos源码赏析(四):基于boost::asio的httpserver架构_第16张图片

15 基于qt的界面的boost::asio实现的post测试小工具

结语

    本次我们随着eos代码的更新,调整了一些分析策略,当然以后的源码分析也不会一成不变。先eos命令行工具入手,查看cleos网络通信的实现,并具体到boost::asio如何实现一个httpserver,最后基于boost::asioqt界面库,做一个小工具用来测试http post相关内容,如前文所说下一篇准备针对cleos的一些命令参数进行相关分析当然本文全以个人理解进行编写,如理解有误或者有哪些意见,还请各位不吝赐教.

长按以下二维码,关注本公众号,一起学习eos开发.

eos源码赏析(四):基于boost::asio的httpserver架构_第17张图片

                      微信公众号

     有任何疑问或者指教请添加本人个人公众号,当然有对eos开发感兴趣的也可以添加,备注eos开发,拉你进群.

eos源码赏析(四):基于boost::asio的httpserver架构_第18张图片

个人微信帐号



你可能感兴趣的:(eos源码赏析(四):基于boost::asio的httpserver架构)