zoukankan      html  css  js  c++  java
  • 基于boost asio实现的支持ssl的通用socket框架

    情景分析
       现已存在一个可用稳定的异步客户端类http_client_base,该类基于boost asio实现了连接服务器,发送请求,获取响应和解析http数据等操作,该类的大致实现框架如下
      1class http_client_base
      2{
      3public:
      4    http_client_base(boost::asio::io_service& io_service)
      5        :resolver_(io_service),socket_(io_service)
      6    
      7    }

      8    
      9    void async_connect(const std::string& address,const std::string& port)
     10    {    
     11        boost::asio::ip::tcp::resolver::query query(address, port);
     12        resolver_.async_resolve(query,boost::bind(&http_client::handle_resolve, this,
     13        asio::placeholders::error,asio::placeholders::iterator));
     14    }

     15    
     16    void async_write(const void* data,size_t size,bool in_place=false)
     17    {
     18        if(!in_place){
     19            //do something
     20            asio::async_write(socket_,request_,
     21                            boost::bind(&http_client::handle_write,this,boost::asio::placeholders::error));
     22        }
    else
     23            asio::async_write(socket_,asio::buffer(data,size),
     24                            boost::bind(&http_client::handle_write,this,boost::asio::placeholders::error));
     25    }

     26    
     27    void async_write_some(const void* data,size_t size,bool in_place=false)
     28    {
     29        if(!in_place){
     30            //do something
     31            boost::asio::async_write(socket_,request_,
     32                                    boost::bind(&http_client::handle_write_some,this,boost::asio::placeholders::error));
     33        }
    else
     34            boost::asio::async_write(socket_,boost::asio::buffer(data,size),
     35                                    boost::bind(&http_client::handle_write_some,this,boost::asio::placeholders::error));
     36    }

     37
     38private:
     39    void handle_resolve(const boost::system::error_code& e,boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
     40    {
     41        if (!e)
     42            boost::asio::async_connect(socket_, endpoint_iterator,
     43                                    boost::bind(&http_client::handle_connect,this,boost::asio::placeholders::error));
     44        else
     45            onIoError(e);
     46    }

     47    
     48    void handle_connect(const boost::system::error_code& e)
     49    {
     50        if(!e)
     51            onConnect();
     52        else
     53            onIoError(e);
     54    }

     55
     56    void handle_write_some(const boost::system::error_code& e)
     57    {
     58        if(!e)
     59            onWriteSome();
     60        else
     61            onIoError(e);
     62    }

     63
     64    void handle_write(const boost::system::error_code& e)
     65    {
     66        if(!e)
     67            boost::asio::async_read_until(socket_, response_," ",
     68                            boost::bind(&http_client::handle_read_header,this,
     69                            boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));
     70        else
     71            onIoError(e);
     72    }

     73    
     74    void handle_read_header(const boost::system::error_code& e,size_t bytes_transferred)
     75    {
     76        if(!e){
     77            //do something
     78            boost::asio::async_read(socket_,response_,asio::transfer_at_least(1),
     79                            boost::bind(&http_client::handle_read_content,this,
     80                            boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred);                            
     81        }
    else
     82            onIoError(e);
     83    }

     84
     85    void handle_read_content(const boost::system::error_code& e,size_t bytes_transferred)
     86    {
     87        if(!e){
     88            //do something
     89            boost::asio::async_read(socket_,response_,asio::transfer_at_least(1),
     90                                boost::bind(&http_client::handle_read_content,this,
     91                                boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));
     92        }
    else
     93            onIoError(e);
     94    }

     95
     96protected:
     97    virtual void onConnect(){}
     98    virtual void onWriteSome(){}
     99    virtual void onIoError(const boost::system::error_code& e){}
    100
    101private:
    102    boost::asio::ip::tcp::socket socket_;
    103    boost::asio::ip::tcp::resolver resolver_;
    104    boost::asio::streambuf request_;
    105    boost::asio::streambuf response_;
    106}
    ;
       显而易见,http_client_base使用tcp::socket作为底层实现,所以数据是非ssl传输的。现因需求变更,为了数据安全要求使用ssl传输。但boost asio中的ssl::stream类接口和tcp::socket有所不同。其实在非ssl和ssl间,不同的只是读写数据的方法,而数据处理逻辑不变,因此为了重用http_client_base的机制框架和对http数据的解析,那么怎么使http_client_base不作大的改动就支持ssl呢?通过研究boost asio源码发现,async_xxx系列自由函数内部要求读写流实现read_some、async_read_some、write_some和async_write_some4个短读写方法。由于tcp::socket已实现短读写而且ssl::stream是tcp::socket的上层,因此只要设计一个抽象的基类流,使之支持read_some、async_some_read、write_some和async_write_some即可,而实现使用dynamic_cast转到兄弟基类tcp::socket或ssl::stream,再调用它们对应的同名短读写方法;另外还需要给出获取最底层socket的接口,以支持async_connect和connect方法。因此针对这一设计实现,则要求派生类必须同时从抽象基类和其兄弟基类tcp::socket或ssl::stream继承。

    框架实现 
      1template<typename T>
      2class boost_socket_base
      3{
      4public:
      5    typedef boost::asio::ssl::stream<T> ssl_socket_base_t;
      6    typedef T socket_base_t;
      7
      8protected:
      9    boost_socket_base()
     10        :tb_(boost::indeterminate)
     11    {
     12    }

     13
     14public:
     15    virtual ~boost_socket_base() {}
     16
     17    ssl_socket_base_t* get_ssl_socket()
     18    {
     19        if(tb_){
     20            BOOST_ASSERT(ss_);        
     21            return ss_;
     22        }
    else if(!tb_)
     23            return NULL;
     24        else{
     25            if(ss_=dynamic_cast<ssl_socket_base_t*>(this))
     26                tb_ = true;
     27            return ss_;
     28        }
     
     29    }

     30
     31    socket_base_t* get_socket()
     32    {
     33        if(!tb_){
     34            BOOST_ASSERT(s_);        
     35            return s_;
     36        }
    else if(tb_)
     37            return NULL;
     38        else{
     39            if(s_=dynamic_cast<socket_base_t*>(this))
     40                tb_ = false;
     41            return s_;
     42        }

     43    }

     44
     45    void reset()
     46    {    tb_ = boost::indeterminate; }
     47
     48    typename T::lowest_layer_type& lowest_layer()
     49    {
     50        ssl_socket_base_t* p = get_ssl_socket();
     51        if(p) 
     52            return p->lowest_layer();
     53        else
     54            return get_socket()->lowest_layer();
     55    }

     56
     57    template <typename MutableBufferSequence>
     58    std::size_t read_some(const MutableBufferSequence& buffers)
     59    {
     60        ssl_socket_base_t* p = get_ssl_socket();
     61        if(p) 
     62            return p->read_some(buffers);
     63        else
     64            return get_socket()->read_some(buffers);
     65    }

     66    
     67    template <typename MutableBufferSequence>
     68    std::size_t read_some(const MutableBufferSequence& buffers,boost::system::error_code& ec)
     69    {
     70        ssl_socket_base_t* p = get_ssl_socket();
     71        if(p) 
     72            return p->read_some(buffers,ec);
     73        else
     74            return get_socket()->read_some(buffers,ec);
     75    }

     76
     77    template <typename MutableBufferSequence, typename ReadHandler>
     78    void async_read_some(const MutableBufferSequence& buffers,BOOST_ASIO_MOVE_ARG(ReadHandler) handler)
     79    {
     80        ssl_socket_base_t* p = get_ssl_socket();
     81        if(p) 
     82            return p->async_read_some(buffers,handler);
     83        else
     84            return get_socket()->async_read_some(buffers,handler);
     85    }

     86
     87    template <typename ConstBufferSequence>
     88    std::size_t write_some(const ConstBufferSequence& buffers,boost::system::error_code& ec)
     89    {
     90        ssl_socket_base_t* p = get_ssl_socket();
     91        if(p) 
     92            return p->write_some(buffers,ec);
     93        else
     94            return get_socket()->write_some(buffers,ec);
     95    }

     96
     97    template <typename ConstBufferSequence>
     98    std::size_t write_some(const ConstBufferSequence& buffers)
     99    {
    100        ssl_socket_base_t* p = get_ssl_socket();
    101        if(p) 
    102            return p->write_some(buffers);
    103        else
    104            return get_socket()->write_some(buffers);
    105    }

    106
    107    template <typename MutableBufferSequence, typename ReadHandler>
    108    void async_write_some(const MutableBufferSequence& buffers,BOOST_ASIO_MOVE_ARG(ReadHandler) handler)
    109    {    
    110        ssl_socket_base_t* p = get_ssl_socket();
    111        if(p) 
    112            return p->async_write_some(buffers,handler);
    113        else
    114            return get_socket()->async_write_some(buffers,handler);
    115    }

    116
    117private:
    118    boost::tribool tb_;
    119    union {
    120        ssl_socket_base_t* ss_;
    121        socket_base_t* s_;
    122    }
    ;
    123}
    ;
       考虑到dynamic_cast转换的性能开销,因此增加了三态逻辑变量tb_和union指针,tb_表示当前this实际指向的对象类型,初始化为indeterminate,true表示ssl socket对象,使用ss_;false表示普通socket对象,使用s_。这样一来,当且仅当tb_为indeterminate时才dynamic_cast。由于这点优化仅对基类指针操作有效,而对派生对象实无必要,所以tb_和union指针设为私有的;而且基类指针可以指向不同的子类对象,所以增加了reset方法重设tb_为indeterminate状态,保证行为的正确性。

    应用改进
       使用boost_socket_base框架后,只须5个地方稍作改动即可。
       1)成员变量:由原来的boost::asio::ip::tcp改为boost_socket_base<boost_tcp_socket>*类型。
    1typedef boost::asio::ip::tcp::socket boost_tcp_socket;
    2boost_socket_base<boost_tcp_socket>* socket_;

       2)构造函数:增加boost::asio::ssl::context* ctx参数,默认为NULL,表示不使用ssl。
    1http_client_base(boost::asio::io_service& io_service,boost::asio::ssl::context* ctx=NULL)
    2    :resolver_(io_service)
    3{
    4        if(ctx)
    5            socket_ = new boost_ssl_socket<boost_tcp_socket>(io_service,*ctx);
    6        else
    7            socket_ = new boost_socket<boost_tcp_socket>(io_service);
    8}

       3)握手处理:与非ssl不同的是,在连接后需要进行握手,握手成功后才回调onConnect。
     1void handle_connect(const boost::system::error_code& e)
     2{
     3    if(!e){
     4        boost_socket_base<boost_tcp_socket>::ssl_socket_base_t* p = socket_->get_ssl_socket();
     5        if(p)
     6            p->async_handshake(boost::asio::ssl::stream_base::client,boost::bind(&http_client::handle_handshake,
     7                           this,boost::asio::placeholders::error));
     8        else
     9            onConnect();
    10    }
    else
    11        onIoError(e);
    12}

    13void handle_handshake(const boost::system::error_code& e)
    14{
    15    if(!e)
    16        onConnect();
    17    else
    18        onIoError(e);
    19}

       4)异步连接:由于async_connect只接受boost::basic_socket类即最底层的socket作为参数,因此需要调用lowest_layer。
    1void handle_resolve(const boost::system::error_code& e,boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
    2{
    3    if (!e)
    4        boost::asio::async_connect(socket_->lowest_layer(), endpoint_iterator,boost::bind(&http_client::handle_connect,this,boost::asio::placeholders::error));
    5    else
    6        onIoError(e);
    7}

       5)async_xxx调用
    :将参数socet_改为*socket_,例如下。
     1void async_write(const void* data,size_t size,bool in_place=false)
     2{
     3    if(!in_place){
     4        //do something
     5        boost::asio::async_write(*socket_,request_,boost::bind(&http_client::handle_write,this,boost::asio::placeholders::error));
     6    }
    else
     7        boost::asio::async_write(*socket_,asio::buffer(data,size),boost::bind(&http_client::handle_write,this,boost::asio::placeholders::error));
     8}

     9void handle_write(const boost::system::error_code& e)
    10{
    11    if(!e)
    12        boost::asio::async_read_until(*socket_, response_, " ",
    13                    boost::bind(&http_client::handle_read_header,this,boost::asio::placeholders::error,asio::placeholders::bytes_transferred));
    14    else
    15        onIoError(e);
    16}
  • 相关阅读:
    Head First设计模式之观察者模式
    Head First设计模式之策略模式
    EF使用Fluent API配置映射关系
    js判断空字符串、null、undefined、空格、中文空格
    从 datetime2 数据类型到 datetime 数据类型的转换产生一个超出范围的值
    System.Data.SqlClient.SqlException: 数据类型 text 和 varchar 在 equal to 运算符中不兼容。
    Content-Type的几种常用数据编码格式
    如何通过Git GUI将自己本地的项目上传至Github
    微信小程序开发之模板
    获取图片的EXIF信息
  • 原文地址:https://www.cnblogs.com/hzcya1995/p/13318605.html
Copyright © 2011-2022 走看看