基于boost asio实现的支持ssl的通用socket框架

情景分析
   现已存在一个可用稳定的异步客户端类http_client_base,该类基于boost asio实现了连接服务器,发送请求,获取响应和解析http数据等操作,该类的大致实现框架如下
  1 class  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_,"\r\n\r\n",
 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、w rite _some和async_write_some即可,而实现使用dynamic_cast转到兄弟基类tcp::socket或ssl::stream,再调用它们对应的同名短读写方法;另外还需要给出获取最底层socket的接口,以支持async_connect和connect方法。因此针对这一设计实现,则要求派生类必须同时从抽象基类和其兄弟基类tcp::socket或ssl::stream继承。

框架实现  
  1 template < typename T >
  2 class  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>*类型。
1 typedef boost::asio::ip::tcp::socket boost_tcp_s ocket;
2 boost_socket_base < boost_tcp_socket >*  socket_;

    2)构造函数 :增加boost::asio::ssl::context* ctx参数,默认为NULL,表示不使用ssl。
1 http_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。
 1 void  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}

13 void  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。
1 void  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_,例如下。
 1 void  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}

 9 void  handle_write( const  boost::system::error_code &  e)
10 {
11    if(!e)
12        boost::asio::async_read_until(*socket_, response_, "\r\n\r\n",
13                    boost::bind(&http_client::handle_read_header,this,boost::asio::placeholders::error,asio::placeholders::bytes_transferred));
14    else
15        onIoError(e);
16}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值