using namespace boost::asio; io_service service; struct connection : boost::enable_shared_from_this{
typedef boost::system::error_code error_code; typedef boost::shared_ptrptr; connection() : sock_(service), started_(true) {} void start(ip::tcp::endpoint ep) {
sock_.async_connect(ep, boost::bind(&connection::on_connect, shared_from_this(),
_1)); }
void stop() { if ( !started_) return; started_ = false; sock_.close();
}
bool started() { return started_; } private:
void on_connect(const error_code & err) { // here you decide what to do with the connection: read or
write if ( !err) do_read();
else stop(); }
void on_read(const error_code & err, size_t bytes) { if ( !started() ) return; std::string msg(read_buffer_, bytes);
if ( msg == "can_login") else if ( msg.find("data ") == 0) else if ( msg == "login_fail")
do_write("access_data"); process_data(msg); stop();
} void on_write(const error_code & err, size_t bytes) {
do_read(); }
void do_read() {
sock_.async_read_some(buffer(read_buffer_), boost::bind(&connection::on_read, shared_from_this(),
_1, _2)); }
void do_write(const std::string & msg) { if ( !started() ) return; // note: in case you want to send several messages before // doing another async_read, you'll need several write
buffers! std::copy(msg.begin(), msg.end(), write_buffer_); sock_.async_write_some(buffer(write_buffer_, msg.size()),
boost::bind(&connection::on_write, shared_from_this(),
_1, _2)); }
void process_data(const std::string & msg) { // process what comes from server, and then perform another
write }
private: ip::tcp::socket sock_; enum { max_msg = 1024 }; char read_buffer_[max_msg]; char write_buffer_[max_msg]; bool started_;
};
int main(int argc, char* argv[]) { ip::tcp::endpoint ep( ip::address::from_string("127.0.0.1"),
8001); connection::ptr(new connection)->start(ep);
}
在所有異步調用中,我們傳遞一個boost::bind仿函數當作參數。這個仿函數內部包含了一個智能指針,指向connection實例。只要有一個異步操作等待時,Boost.Asio會保存boost::bind仿函數的拷貝,這個拷貝保存了指向連接實例的一個智能指針,從而保證connection實例保持活動。問題解決!
當然,connection類僅僅是一個skeleton類;你需要根據你的需求對它進行調整(它看起來會和服務端的情況相當不同)。
你需要注意創建一個新的連接是相當簡單的:connection::ptr(new connection)- >start(ep)。這個方法啟動了到服務端的(異步)連接。當你需要關閉這個連接時,調用stop()。
當實例被啟動時(start()),它將會等待被連接。當連接發生時。on_connect()被調用。如果沒有錯誤發生,它啟動一個read操作(do_read())。當read操作結束時,你解析這個消息;你應用的on_read()看起來會各種各樣。當你寫回一個消息時,你需要把它拷貝到緩沖區,然後像我在do_write()方法中所做的一樣將其發送出去,因為再一次,這個緩沖區需要在這個異步寫操作中一直存活。最後需要注意的一點——當寫回時,你需要指定寫入的數量,否則,整個緩沖區都會被發送出去。
總結
網絡api實際上要大得多,這個章節只是一個參考,當你在實現你自己的網絡應用時,你需要回來查看。
Boost.Asio實現了端點的概念,你可以認為是IP和端口。如果你不知道准確的IP,你可以使用resolver對象將主機名,例如www.yahoo.com轉換為一個或多個IP地址。
我們也可以看到API的核心——socket類。Boost.Asio提供了TCP、UDP和 ICMP的實現。但是你可以用你自己的協議來對它進行擴展;當然,這個工作不適合膽小的人。
異步編程是必要之惡。你會明白為什麼有時候需要它,尤其在寫服務端的時候。調用service.run()來實現異步循環就已經可以讓你很開心,但是有時候你需要更進一步,嘗試使用run_one()、poll()或者poll_one()。
當實現異步時,你可以用你自己方法來異步執行;使用service.post()或者service.dispatch()。
最後,為了使socket和緩沖區(read或者write)在整個異步操作的生命周期中一直活動,我們需要采取特殊的防護措施。你的連接類需要繼承自enabled_shared_from_this,在內部保存它需要的緩沖區,而且每個異步調用都要傳遞一個智能指針給this操作。
下一章會讓你進行實戰操作;在實現回顯客戶端/服務端應用時會有大量的上手編程。