情景分析 現(xiàn)已存在一個(gè)可用穩(wěn)定的異步客戶端類http_client_base,該類基于boost asio實(shí)現(xiàn)了連接服務(wù)器,發(fā)送請(qǐng)求,獲取響應(yīng)和解析http數(shù)據(jù)等操作,該類的大致實(shí)現(xiàn)框架如下
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
private:
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

45
protected:
46
virtual void onConnect()
{}
47
virtual void onWrite()
{}
48
virtual void onIoError(const boost::system::error_code& e)
{}
49
50
private:
51
boost::asio::ip::tcp::socket socket_;
52
boost::asio::ip::tcp::resolver resolver_;
53
boost::asio::streambuf request_, response_;
54
}; 顯而易見(jiàn),http_client_base使用tcp::socket作為底層實(shí)現(xiàn),所以數(shù)據(jù)是非ssl傳輸?shù)摹,F(xiàn)因需求變更,為了數(shù)據(jù)安全要求使用ssl傳輸。但boost asio中的ssl::stream類接口和tcp::socket有所不同。其實(shí)在非ssl和ssl間,不同的只是讀寫(xiě)數(shù)據(jù)的方法,而數(shù)據(jù)處理邏輯不變,因此為了重用http_client_base的機(jī)制框架和對(duì)http數(shù)據(jù)的解析,那么怎么使http_client_base不作大的改動(dòng)就支持ssl呢?通過(guò)研究asio源碼發(fā)現(xiàn),
async_xxx系列自由函數(shù)內(nèi)部要求讀寫(xiě)流實(shí)現(xiàn)read_some、async_read_some、write_some和async_write_some4個(gè)短讀寫(xiě)方法。由于tcp::socket已實(shí)現(xiàn)短讀寫(xiě)而且ssl::stream是tcp::socket的上層,因此只要設(shè)計(jì)一個(gè)抽象的基類流,使之支持read_some、async_some_read、w
rite_some和async_write_some即可,而實(shí)現(xiàn)使用dynamic_cast轉(zhuǎn)到兄弟基類tcp::socket或ssl::stream,再調(diào)用它們對(duì)應(yīng)的同名短讀寫(xiě)方法;另外還需要給出獲取最底層socket的接口,以支持async_connect和connect方法。因此針對(duì)這一設(shè)計(jì)實(shí)現(xiàn),則要求派生類必須同時(shí)從抽象基類和其兄弟基類tcp::socket或ssl::stream繼承。
框架實(shí)現(xiàn) 基類模板
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
public:
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
91
private:
92
boost::tribool tb_;
93
union
{
94
ssl_socket_base_t* ss_;
95
socket_base_t* s_;
96
};
97
}; 考慮到dynamic_cast轉(zhuǎn)換的性能開(kāi)銷,因此增加了三態(tài)邏輯變量tb_和union指針,tb_表示當(dāng)前this實(shí)際指向的對(duì)象類型,初始化為indeterminate,true表示ssl socket對(duì)象,使用ss_;false表示普通socket對(duì)象,使用s_。這樣一來(lái),當(dāng)且僅當(dāng)tb_為indeterminate時(shí)才dynamic_cast。由于這點(diǎn)優(yōu)化僅對(duì)基類指針操作有效,而對(duì)派生對(duì)象實(shí)無(wú)必要,所以tb_和union指針設(shè)為私有的;而且基類指針可以指向不同的子類對(duì)象,所以增加了reset方法重設(shè)tb_為indeterminate狀態(tài),保證行為的正確性。
子類模板
1
template<typename T>
2
class boost_ssl_socket : public boost_socket_base<T>
3
, public boost::asio::ssl::stream<T>
4

{
5
public:
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
13
template<typename T>
14
class boost_socket : public boost_socket_base<T>
15
, public T
16

{
17
public:
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。
應(yīng)用改進(jìn) 使用上面ssl socket框架后,只須5個(gè)地方稍作改動(dòng)即可。
1)成員變量:由原來(lái)的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)構(gòu)造函數(shù):增加boost::asio::ssl::context* ctx參數(shù),默認(rèn)為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不同的是,在連接后需要進(jìn)行握手,握手成功后才回調(diào)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作為參數(shù),因此需要調(diào)用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調(diào)用:將參數(shù)socket_改為*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
春秋十二月 閱讀(12310)
評(píng)論(2) 編輯 收藏 引用 所屬分類:
Opensrc