您的位置:首页 > 其它

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

2013-07-03 23:52 453 查看
基于boost asio实现的支持ssl的通用socket框架
refer to : http://www.cppblog.com/qinqing1984/archive/2013/03/20/198644.html


情景分析

现已存在一个可用稳定的异步客户端类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

}

posted on 2013-03-20 20:47 春秋十二月 阅读(1770) 评论(2) 编辑 收藏 引用 所属分类: library
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: