boost::asio的类继承体系

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(图中的下半部分).

boost::asio的类继承体系

在客户端需要连接服务器的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的具体操作.

时序

boost::asio的类继承体系

从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的代码,留在下一次讨论。

上一篇:libev与多线程


下一篇:block设备驱动之内核机制