您的位置:首页 > 其它

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

2017-07-06 19:56 417 查看
情景分析
   现已存在一个可用稳定的异步客户端类http_client_base,该类基于boost asio实现了连接服务器,发送请求,获取响应和解析http数据等操作,该类的大致实现框架如下

  1

class http_client_base
  2

{
  3

public:
  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


 38

private:
 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


 96

protected:
 97

    virtual void onConnect(){}
 98

    virtual void onWriteSome(){}
 99

    virtual void onIoError(const boost::system::error_code& e){}
100


101

private:
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继承。

框架实现 

  1

template<typename T>
  2

class boost_socket_base
  3

{
  4

public:
  5

    typedef boost::asio::ssl::stream<T> ssl_socket_base_t;
  6

    typedef T socket_base_t;
  7


  8

protected:
  9

    boost_socket_base()
 10

        :tb_(boost::indeterminate)
 11

    {
 12

    }
 13


 14

public:
 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


117

private:
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_socket;
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

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: