随笔-156  评论-223  文章-30  trackbacks-0
情景分析
   现已存在一个可用稳定的异步客户端类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    
 27private:
 28        
 29    void handle_connect(const boost::system::error_code& e)
 30    {
 31        if(!e)
 32            onConnect();
 33        else
 34            onIoError(e);
 35    }

 36
 37    void handle_write(const boost::system::error_code& e)
 38    {
 39        if(!e)
 40            onWrite();
 41        else
 42            onIoError(e);
 43    }

 44    
 45protected:
 46    virtual void onConnect(){}
 47    virtual void onWrite(){}
 48    virtual void onIoError(const boost::system::error_code& e){}
 49
 50private:
 51    boost::asio::ip::tcp::socket socket_;
 52    boost::asio::ip::tcp::resolver resolver_;
 53    boost::asio::streambuf request_, response_;
 54}
;
   显而易见,http_client_base使用tcp::socket作为底层实现,所以数据是非ssl传输的。现因需求变更,为了数据安全要求使用ssl传输。但boost asio中的ssl::stream类接口和tcp::socket有所不同。其实在非ssl和ssl间,不同的只是读写数据的方法,而数据处理逻辑不变,因此为了重用http_client_base的机制框架和对http数据的解析,那么怎么使http_client_base不作大的改动就支持ssl呢?通过研究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
13public:
14    virtual ~boost_socket_base()
15    { }
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    typename T::lowest_layer_type& lowest_layer()
46    {
47        ssl_socket_base_t* p = get_ssl_socket();
48        return p ? p->lowest_layer() : get_socket()->lowest_layer();
49    }

50    
51    template <typename MutableBufferSequence>
52    std::size_t read_some(const MutableBufferSequence& buffers,boost::system::error_code& ec)
53    {
54        ssl_socket_base_t* p = get_ssl_socket();
55        return p ? p->read_some(buffers) : get_socket()->read_some(buffers,ec);
56    }

57
58    template <typename MutableBufferSequence>
59    std::size_t read_some(const MutableBufferSequence& buffers)
60    {
61        //与上面相同,但不带ec
62    }

63    
64    template <typename MutableBufferSequence, typename ReadHandler>
65    void async_read_some(const MutableBufferSequence& buffers,BOOST_ASIO_MOVE_ARG(ReadHandler) handler)
66    {
67        ssl_socket_base_t* p = get_ssl_socket();
68        return p ? p->async_read_some(buffers,handler) : get_socket()->async_read_some(buffers,handler);
69    }

70
71    template <typename ConstBufferSequence>
72    std::size_t write_some(const ConstBufferSequence& buffers,boost::system::error_code& ec)
73    {
74        ssl_socket_base_t* p = get_ssl_socket();
75        return p ? p->write_some(buffers,ec) : get_socket()->write_some(buffers,ec);
76    }

77    
78    template <typename ConstBufferSequence>
79    std::size_t write_some(const ConstBufferSequence& buffers)
80    {
81        //与上面相同,但不带ec
82    }

83    
84    template <typename MutableBufferSequence, typename ReadHandler>
85    void async_write_some(const MutableBufferSequence& buffers,BOOST_ASIO_MOVE_ARG(ReadHandler) handler)
86    {    
87        ssl_socket_base_t* p = get_ssl_socket();
88        return p ? p->async_write_some(buffers,handler) : get_socket()->async_write_some(buffers,handler);
89    }

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

   子类模板 
 1template<typename T> 
 2class boost_ssl_socket : public boost_socket_base<T>
 3                       , public boost::asio::ssl::stream<T>
 4{
 5public:
 6    typedef boost::asio::ssl::stream<T> base2;
 7    
 8    boost_ssl_socket(boost::asio::io_service& io_service,boost::asio::ssl::context& ctx)
 9        :base2(io_service,ctx)
10    { }
11}
;
12
13template<typename T>
14class boost_socket : public boost_socket_base<T>
15                   , public T
16{
17public:
18    typedef T base2;
19    
20    boost_socket(boost::asio::io_service& io_service)
21        :base2(io_service)
22    { }
23}
;
  boost_ssl_socket为ssl套接字类模板,boost_socket为普通套接字类模板,使用多重继承,第1基类为boost_socket_base<T>,第2基类分别为asio:ssl:stream<T>和T。

应用改进
   使用上面ssl socket框架后,只须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调用
:将参数socket_改为*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_, "\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}
posted on 2013-03-20 20:47 春秋十二月 阅读(12214) 评论(2)  编辑 收藏 引用 所属分类: Opensrc

评论:
# re: 基于boost asio实现的支持ssl的通用socket框架 2013-03-21 11:47 | wem
学习了  回复  更多评论
  
# re: 基于boost asio实现的支持ssl的通用socket框架 2013-06-08 09:36 |
最近才看过boost的asio ,现在能看懂点这篇博客了。  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理