asio概述
周末看了asio的网络库,和其他网络库相比,asio更复杂,更加的OO,而且用模板抽象了网络编程的通用操作。
简单来说,Boost.Asio是一个跨平台的、主要用于网络和其他一些底层输入/输出编程的C++库。
Boost.Asio在2003被开发出来,然后于2005年的12月引入到Boost 1.35版本中。原作者是Christopher M. Kohlhoff.
关键词: 跨平台, 稳定, 其次是性能.
简单的例子
展示一个连接并发送字符串的例子, 最要的是如何编译和运行.
server
下面这段代码是从Boost.asio网站上抄过来的
http://think-async.com/Asio/boost_asio_1_10_6/doc/html/boost_asio/examples/cpp03_examples.html
// // async_tcp_echo_server.cpp // ~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #include <cstdlib> #include <iostream> #include <boost/bind.hpp> #include <boost/asio.hpp> using boost::asio::ip::tcp; class session { public: session(boost::asio::io_service& io_service) : socket_(io_service) { } tcp::socket& socket() { return socket_; } void start() { socket_.async_read_some(boost::asio::buffer(data_, max_length), boost::bind(&session::handle_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); } void handle_read(const boost::system::error_code& error, size_t bytes_transferred) { if (!error) { boost::asio::async_write(socket_, boost::asio::buffer(data_, bytes_transferred), boost::bind(&session::handle_write, this, boost::asio::placeholders::error)); } else { delete this; } } void handle_write(const boost::system::error_code& error) { if (!error) { socket_.async_read_some(boost::asio::buffer(data_, max_length), boost::bind(&session::handle_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); } else { delete this; } } private: tcp::socket socket_; enum { max_length = 1024 }; char data_[max_length]; }; class server { public: server(boost::asio::io_service& io_service, short port) : io_service_(io_service), acceptor_(io_service, tcp::endpoint(tcp::v4(), port)) { session* new_session = new session(io_service_); acceptor_.async_accept(new_session->socket(), boost::bind(&server::handle_accept, this, new_session, boost::asio::placeholders::error)); } void handle_accept(session* new_session, const boost::system::error_code& error) { if (!error) { new_session->start(); new_session = new session(io_service_); acceptor_.async_accept(new_session->socket(), boost::bind(&server::handle_accept, this, new_session, boost::asio::placeholders::error)); } else { std::cout <<"handle_acceptor error" << std::endl; delete new_session; } } private: boost::asio::io_service& io_service_; tcp::acceptor acceptor_; }; int main(int argc, char* argv[]) { try { if (argc != 2) { std::cerr << "Usage: async_tcp_echo_server <port>\n"; return 1; } boost::asio::io_service io_service; using namespace std; // For atoi. server s(io_service, atoi(argv[1])); io_service.run(); } catch (std::exception& e) { std::cerr << "Exception: " << e.what() << "\n"; } return 0; }
client
#include <string.h> #include <iostream> #include <boost/asio.hpp> #include <boost/bind.hpp> #include <boost/shared_ptr.hpp> #include <boost/array.hpp> typedef boost::shared_ptr<boost::asio::ip::tcp::socket> socket_ptr_t; typedef boost::array<char, 128> buffer_t; typedef boost::shared_ptr<buffer_t> buffer_ptr_t; void on_read(boost::system::error_code ec, std::size_t len, socket_ptr_t socket_ptr, buffer_ptr_t buffer_ptr) { if (ec) std::cout << "async write error:" << ec.message() << std::endl; else { std::cout << "on_read(), " << std::string((char*)buffer_ptr->begin(), len) << std::endl; } } void on_write(boost::system::error_code ec, std::size_t len, socket_ptr_t socket_ptr, buffer_ptr_t buffer_ptr) { if (ec) std::cout << "async write error:" << ec.message() << std::endl; else { std::cout << "on_write(), write completely: " << len << std::endl; socket_ptr->async_read_some(boost::asio::buffer(buffer_ptr.get(), buffer_t::size()), boost::bind(&on_read, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, socket_ptr, buffer_ptr)); } } void on_connect(boost::system::error_code ec, socket_ptr_t socket_ptr) { if (ec) std::cout << "asio client: async connect error:" << ec.message() << std::endl; else { std::cout << "on_connect(), " << socket_ptr->remote_endpoint() << std::endl; buffer_ptr_t buffer_ptr(new buffer_t); strncpy((char*)buffer_ptr->begin(), "abcdefg", buffer_t::size()); socket_ptr->async_write_some(boost::asio::buffer(buffer_ptr.get(), strlen((char*)buffer_ptr->begin())), boost::bind(&on_write, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, socket_ptr, buffer_ptr)); } } int main() { boost::asio::io_service ios; boost::asio::ip::tcp::endpoint addr(boost::asio::ip::address::from_string("127.0.0.1"), 12345); socket_ptr_t conn_socket_ptr(new boost::asio::ip::tcp::socket(ios)); conn_socket_ptr->async_connect(addr, boost::bind(&on_connect, boost::asio::placeholders::error, conn_socket_ptr)); ios.run(); std::cout << "press enter key..."; std::cin.get(); return 0; }
编译运行
c++ echo_server.cpp -I~/Code/boost_1_55_0 -L~/Code/boost_1_55_0/stage/lib/ -lboost_system-mt -lboost_thread-mt -o echo_server -g -O0 c++ asio_client.cpp -I~/Code/boost_1_55_0 -L~/Code/boost_1_55_0/stage/lib/ -lboost_system-mt -lboost_thread-mt -o asio_client -g -O0
运行
server: max@max-gentoo ~/Study/Boost $ ./echo_server 12345 client: max@max-gentoo ~/Study/Boost $ ./asio_client on_connect(), 127.0.0.1:12345 on_write(), write completely: 7 on_read(), abcdefg press enter key...
类继承体系分析
asio不仅支持多平台, 还支持多协议tcp和udp, 还支持多线程和同步异步和定时器, 如何抽象的呢? asio抽象了两个继承体系: 存储套接字的实体(图中的上半部分)和操作相应套接字的service(图中的下半部分).
在客户端需要连接服务器的12345端口. 首先指定套接字和service:
boost::asio::io_service ios; socket_ptr_t conn_socket_ptr(new boost::asio::ip::tcp::socket(ios));
boost::asio::ip::tcp::socket的类型为:
//tcp.hpp typedef basic_stream_socket<tcp> socket;
而, basic_stream_socket正是组合procotol和service两种服务的模板类.第二个模板参数的默认值是stream_socket_service:
template <typename Protocol, typename StreamSocketService = stream_socket_service<Protocol> > class basic_stream_socket : public basic_socket<Protocol, StreamSocketService>
basic_stream_socket继承自basic_socket, basic_socket是个很重要的类, 所有和套接字相关的累都继承自这个类, 比如: basic_seq_socket, basic_raw_socket.
template <typename Protocol, typename SocketService> class basic_socket : public basic_io_object<SocketService>, public socket_base
basic_socket同时继承自socket_base和basic_io_object: socket_base封装了socket的基本操作,basic_io_object封装了io的具体操作.
时序
从client端发起异步连接开始.
conn_socket_ptr->async_connect(addr, boost::bind(&on_connect, boost::asio::placeholders::error, conn_socket_ptr));
async_connect需要两个参数:第一个是地址信息;第二个是连接成功后的回调函数。
async_connect方法是在conn_socket父类basic_socket中定义的。显而易见basic_socket需要提供各种socket的操作如: open, bind,connect等套接字的方法.
basic_socket真正发起async_connect操作是通过相应的service完成的: get_servie()返回这种套接字对应的service.
这个service是在何时绑定的呢?还要从类的继承体系中寻找。
template <typename Protocol, typename StreamSocketService = stream_socket_service<Protocol> > class basic_stream_socket : public basic_socket<Protocol, StreamSocketService>
可见,get_service返回的是stream_socket_service。这个service和protocol是配套的。
stream_socket_service中的async_connect是通过代理类service_impl实现的。
stream_socket_service是个通用的类,适用于各个平台,具体平台的操作是通过具体的service_impl来完成。
#if defined(BOOST_ASIO_WINDOWS_RUNTIME) typedef detail::winrt_ssocket_service<Protocol> service_impl_type; #elif defined(BOOST_ASIO_HAS_IOCP) typedef detail::win_iocp_socket_service<Protocol> service_impl_type; #else typedef detail::reactive_socket_service<Protocol> service_impl_type; #endif
可见,不同平台的处理类通过宏和typedef重新定义为了_impe_type类型。
Linux 平台的_impe_type是reactive_socket_service。
reactive_socket_service发起start_connect_op操作,最终调用基类reactive_socket_service_base的start_connet_op,伪代码如下:
start_connet_op() { socket_ops::connect(impl.socket_, addr, addrlen, op->ec_); reactor_.start_op(reactor::connect_op, impl.socket_, impl.reactor_data_, op, is_continuation, false) }
先非阻塞的调用connect方法,然后start_op,像reactor中插入一个回调(闭包)。
这种 nonblocking + callback的模式很经典,上层的任何操作都是nonblocking的,为了防止底层的reactor陷入阻塞而耽误其他的操作,同时上层可以立即返回作其他的事情,而底层通过回调来返回结果。
请记住 nonblcking + callback。
至于start_op的代码,留在下一次讨论。