asio

asio

基于操作系统提供的异步机制,不要求使用多线程和锁,采用前摄器proactor设计模式,实现了可移植的异步IO操作
目前asio主要关注于网络通信方面,封装了socket API,提供了TCP、UDP、ICMP等网络通信协议,但asio的异步操作不局限于网络编程,还支持UNIX信号,定时器,串口读写,SSL(安装openssl)等功能,还可以扩展到其他有异步操作的领域

位于命名空间boost::asio

#include<boost/asio.hpp>
using namespace boost::asio;

asio库基于前摄器模式封装了操作系统的selectpollepollkqueue等机制,实现了异步IO模型
核心类是io_service,相当于前摄器模式中的proactor

在同步模式下,程序发起一个IO操作,向io_service提交请求,io_service将操作交给操作系统,同步等待
当IO操作完成时,操作系统通知io_service,然后io_service再把结果返回给程序,完成同步流程

在异步模式下,程序除了发起IO操作,还定义一个完成处理回调,io_service将IO操作交给操作系统执行,立即返回
调用io_servicerun()成员函数等待异步操作完成,io_service向操作系统获取结果,再调用回调

handler

是符合某种函数签名的回调函数,handler必须是可拷贝的,因为io_service会存储handler的拷贝
asio库中,handler有以下三种:

  • 只有一个error_code参数
    标志某个异步事件完成
  • error_codesignal_number两个参数
    标志发生一个UNIX信号事件
  • error_codebytes_transferred两个参数
    标志某个读写操作完成,可读性的数据字节数是bytes_transferred,常用于socket回调

io_service

类原型

class io_service:private noncopyable{
public:
    std::size_t run(); // 阻塞执行事件循环
    std::size_t run_one(); // 至少阻塞执行一个handler

    std::size_t poll(); // 非阻塞,执行ready的handler
    std::size_t poll_one(); // 至少执行一个ready的handler

    void stop(); // 停止事件循环
    bool stopped()const; // 事件循环是否已经停止
    bool reset()const; // 重启事件循环

    unspecified dispatch(Handler handler); // 要求异步执行一个handler
    unspecified post(Handler handler); // 要求异步执行一个handler

    class strand; // 内部线程类
    class work; // 表示有工作在进行
};

io_service类表示系统里的异步处理机制(如epoll),必须在asio库中其他对象初始化前初始化,其他对象向其提交异步操作的handler,成员函数run()启动事件循环,阻塞等待所有注册到io_service的事件完成

strand

asio库是基于操作系统的异步IO模型,不直接使用系统线程,而是定义了一个自己的线程概念:strand
用来序列化异步操作,保证异步代码在多线程环境中正确执行,无需使用互斥量
类原型

class io_service::strand{
public:
    explicit strand(io_service& io);
    io_service& get_io_service();

    unspecified dispatch(Handler handler);
    unspecified post(Handler handler);
    unspecified wrap(Handler handler); // 包装一个handler

    bool running_in_this_thread()const;
};

wrap()包装一个函数,返回一个相同签名的函数对象,线程安全地在strand中执行

在一个线程中使用io_service是安全的,不需要使用strand进行保护
但若多个线程对一个io_service对象执行run(),那么同一个handler就有可能存在线程竞争,需要使用strand保护

work

io_service里注册的所有事件都完成时,会退出事件循环,若想继续运行,以处理将来可能发生的异步事件
内部类work可以完成这样的工作,在构造函数中启动一个可用的工作,在析构函数中停止工作
类原型

class io_service::work{
public:
    explicit work(io_service& io_serv);
    ~work();
    boost::asio::io_service& get_io_service();
};

mutable_buffer 和 const_buffer

IO操作经常使用数据缓冲区,相当于一片指定的内存区域,asio库专门用两个类来表示这个概念

class mutable_buffer{
public:
    mutable_buffer();
    mutable_buffer(void* data, std::size_t size);
private:
    void* data_;
    std::size_t size_;
};

class const_buffer{
public:
    const_buffer();
    const_buffer(const void* data, std::size_t size);
    const_buffer(const mutable_buffer& b);
private:
    void* data_;
    std::size_t size_;
};

另有两个buffer类,将上面两个类适配为基本的容器概念,并提供begin/end操作

class mutable_buffer_1:public mutable_buffer{
public:
    const_iterator begin()const;
    const_iterator end()const;
};

class const_buffer_1:public const_buffer{
public:
    const_iterator begin()const;
    const_iterator end()const;
};

通常使用工厂函数buffer()生产buffer对象,包装常用的C++容器类型
buffer()提供多种重载形式,适用于各种类型,如:

unspecified buffer(void* data, std::size_t size_in_bytes);
unspecified buffer(std::vector& data);

asio库还提供了几个函数以操作buffer

std::size_t buffer_size(const buffer_type& b);
T* buffer_cast(const buffer_type& b);
std::size_t buffer_copy(const mutable_buffer& target, const const_buffer& source);

错误处理

asio库使用system库的error_codesystem_error表示程序运行时的错误
基本上所有的异步操作函数都有两种重载形式
一种是有一个error_code的输出参数
另一种是抛出system_error异常
io_service成员函数的error_code形式如下

std::size_t run(boost::system::error_code& ec);
std::size_t run_one(boost::system::error_code& ec);
std::size_t poll(boost::system::error_code& ec);
std::size_t poll_one(boost::system::error_code& ec);

跟踪日志

异步代码的调试是复杂的,asio库为此提供了handler跟踪机制
只要在头文件<boost/asio.hpp>前定义宏BOOST_ASIO_ENABLE_HANDLER_TRACKING,就会向标准流cerr输出运行日志,使用重定向功能也可以写入指定的日志文件
跟踪日志以|分为4个字段:tag|timestamp|action|description
第一个字段是标记字符串,目前总是@asio,第二个字段是UNIX时间戳,精确到毫秒,最后两个字段很重要,记录了异步代码的具体动作,定义如下:

字段 说明
n n号handler正在执行某些操作
>n 进入n号handler,description表示入口参数
<n 离开n号handler,无description
!n 发生异常,离开n号handler
~n n号handler未被调用就被销毁
n*m n号handler创建了一个新的m号handler

asio还提供一个可视化日志脚本handlerrviz.pl,位于libs/asio/tools/
需要图形工具GraphViz的支持,安装命令sudo apt-get install graphviz

perl handlerviz.pl < out.log | dot -Tpng > out.png
perl handlerviz.pl < out.log | dot -Tpdf > out.pdf

asio.ip

折腾线程池,还不如培养异步控制流思维
system_context和co_await用起来

网络通信

asio库支持TCP、UDP和ICMP等通信协议
命名空间boost::asio::ip中提供了网络通信方面的函数和类

ip::address类

表示IP地址,支持ipv4和ipv6两种地址

类原型

class address{
public:
    address();
    address(const address& other);

    bool is_v4()const;
    bool is_v6()const;
    bool is_loopback()const;

    ip::address_v4 to_v4()const;
    ip::address_v6 to_v6()const;

    string to_string()const;

    static address from_string(const char* str);
    static address from_string(const string& str);

    friend bool operator==(const address& a1, const address& a2);
    // ...
private:
    enum{ipv4, ipv6} type_;
    boost::asio::ip::address_v4 ipv4_address_;
    boost::asio::ip::address_v6 ipv6_address_;
};

工厂函数from_string()从字符串生成IP地址
示例

ip::address addr;
addr= addr.from_string("127.0.0.1");
assert(addr.is_v4());
cout<<addr.to_string()<<endl;

addr= addr.from_string("ab:12:34:56");
assert(addr.is_v6());

ip::tcp类

表示TCP协议,定义了用于TCP通信常用类型,位于<boost/asio/ip/tcp.hpp>

类型 说明
endpoint 端口类
socket 套接字类
iostream 流类
acceptor 接收器类
resolver 解析器类

类原型

class tcp{
public:
    typedef basic_endpoint<tcp> endpoint;
    typedef basic_stream_socket<tcp> socket;
    typedef basic_socket_acceptor<tcp> acceptor;
    typedef basic_resolver<tcp> resolver;
    typedef basic_resolver_query<tcp> resolver_query;
    typedef basic_resolver_iterator<tcp> resolver_iterator;
    typedef basic_socket_iostream<tcp> iostream;

    int type()const; // 获取协议类型
    int protocol()const; // 获取协议标志

    static tcp v4(); // 返回ipv4的tcp对象
    static tcp v6(); // 返回ipv6的tcp对象
};

ip::tcp::endpoint类

basic_endpoint的TCP协议特化,用于构造一个可用于socket通信的socket端点对象
类原型

template<typename InternetProtocol>
class basic_endpoint{
public:
    typedef InternetProtocol protocol_type;
    basic_endpoint(const InternetProtocol& internet_protocol,
                   unsigned short port_num);
    basic_endpoint(const ip::address& addr, unsigned short port_num);

    basic_endpoint(basic_endpoint&& other);
    basic_endpoint& operator=(const basic_endpoint& other);

    protocol_type protocol()const; // 获取协议

    unsigned short port()const; // 获取端口号
    void port(unsigned short port_num); // 设置端口号

    ip::address address()const; // 获取地址
    void address(ip::address& addr); // 设置地址
};

端点地址和端口号可分别用address()port()获取
示例

ip::address addr;
addr= addr.from_string("127.0.0.1");
ip::tcp::endpoint ep(addr,6688);
assert(ep.address()==addr);
assert(ep.port()==6688);

ip::tcp::socket类

basic_stream_socket的TCP协议特化
类原型

template<typename Protocol, ...>
class basic_stream_socket{
public:
    typedef Protocol protocol_type;
    typedef typename Protocol::endpoint endpoint_type;

    // 构造函数
    explicit basic_stream_socket(io_service& io_serv);
    basic_stream_socket(io_service& io_serv, const protocol_type& protocol);
    basic_stream_socket(io_service& io_serv, const endpoint_type& endpoint);

    // 移动构造函数
    basic_stream_socket(basic_stream_socket&& other);
    basic_stream_socket& operator=(basic_stream_socket&& other);

    // 打开关闭端口,取消操作
    void open(const protocol_type& protocol=protocol_type());
    bool is_open()const;
    void close();
    void shutdown(shutdown_type what);
    void cancel();

    std::size_t available()const; // 可读取的字节数
    void bind(const endpoint_type& endpoint); // 绑定endpoint

    // 连接endpoint
    void connect(const endpoint_type& peer_endpoint);
    void async_connect(const endpoint_type& peer, handler);

    // 设置socket选项
    void set_option(const SettableSocketOption& option);
    void get_option(GettableSocketOption& option)const;

    // 是否非阻塞
    bool non_blocking()const;
    bool non_blocking(bool mode);

    // 本地endpoint和远端endpoint
    endpoint_type local_endpoint()const;
    endpoint_type remote_endpoint()const;

    // 发送数据
    std::size_t send(const ConstBuffer& buffers);
    void async_send(const ConstBuffer& buffers, handler);
    // 接收数据
    std::size_t recive(const MutableBuffer& buffers);
    void async_receive(const MutableBuffer& buffers, handler);

    // 发送数据
    std::size_t write_some(const ConstBuffer& buffers);
    void async_write_some(const ConstBuffer& buffers);
    // 接收数据
    std::size_t read_some(const MutableBuffer& buffers);
    void async_read_some(const MutableBuffer& buffers, handler);
private:
};

两种读写数据的函数,内部用的都是系统函数sendmsg()recvmsg()
但是send()/receive()多一种使用socket_base::message_flags参数的重载形式
读写函数的参数都是buffer类型,可以用buffer()包装各种容器适配
区别在于send()/write_some()参数应该是一个可读buffer,而receive()/read_some()参数是可写buffer

异步读写函数asycn_xxx()可以使用两种形式的handler

void handler(const error_code& ec, std::size_t bytes_transferred);
void handler(const error_code& ec);

ip::tcp::acceptor类

用于服务器端,在指定的端口接收连接请求,配合socket类完成通信
basic_socket_acceptor的TCP协议特化
类原型

template<typename Protocol, ...>
class basic_socket_acceptor{
public:
    typedef Protocol protocol_type;
    typedef typename Protocol::endpoint endpoint_type;

    // 构造函数
    explicit basic_socket_acceptor(io_service& io_serv);
    basic_socket_acceptor(io_service& io_serv, const protocol_type& protocol);
    basic_socket_acceptor(io_service& io_serv, const endpint_type& endpoint, bool reuse_addr=true);

    // 移动构造函数
    basic_socket_acceptor(basic_socket_acceptor&& other);
    basic_socket_acceptor& operator=(basic_socket_acceptor&& other);

    // 打开关闭端口,取消操作
    void open(const protocol_type& protocol=protocol_type());
    bool is_open()const;
    void close();
    void cancel();

    // 绑定endpoint,监听端口
    void bind(const endpoint_type& endpoint);
    void listen(int backlog=socket_base::max_connections);

    // 是否是非阻塞的
    bool non_blocking()const;
    void non_blocking(bool mode);

    // 设置socket选项
    void set_option(const SettableSocketOption& option);
    void get_option(GettableSocketOption& option);

    // 本地endpoint
    endpoint_type local_endpoint()const;

    // 接收请求
    void accept(socket& peer);
    void async_accept(socket& peer, handler);
    void accept(socket& peer, endpoint_type& peer_endpoint);
    void async_accept(socket& peer, endpoint_type& peer_endpoint, handler);
};

可以像使用socket API那样使用acceptor
open()打开端口,bind()绑定endpoint后,再用listen()监听端口
或更方便地使用构造函数传入endpoint直接完成这3个动作

同步通信

服务器端

int main(){
    try{
        typedef ip::tcp::acceptor acceptor_type;
        typedef ip::tcp::endpoint endpoint_type;
        typedef ip::tcp::socket socket_type;
        cout<<"server start..."<<endl;

        io_service io;
        acceptor_type acceptor(io, endpoint_type(ip::tcp::v4(),6688))
        cout<<acceptor.local_endpoint().address<<endl;

        for(;;){
            socket_type sock(io);
            acceptor.accept(sock);
            cout<<"client:";
            cout<<sock.remote_endpoint().address()<<endl;
            sock.send(buffer("hello asio"));
        }
    }catch(std::exception& e){
        cout<<e.what()<<endl;
    }
}

客户端

int main(){
    try{
        typedef ip::tcp::acceptor acceptor_type;
        typedef ip::tcp::endpoint endpoint_type;
        typedef ip::tcp::socket socket_type;
        cout<<"client start..."<<endl;

        io_service io;
        socket_type sock(io);

        endpoint_type ep(address_type::from_string("127.0.0.1"),6688);

        sock.connect(ep);
        cout<<sock.available()<<endl;

        vector<char> str(sock.available()+1, 0);
        sock.receive(buffer(str)); // 包装缓冲区,接收数据
        /*
        vector<char> str(5,0)
        error_code ec;
        for(;;){
            sock.read_some(buffer(str), ec);
            if(ec){
                break;
            }
            cout<<&str[0];
        }
        */

        cout<<"receive from:"<<sock.remote_endpoint().address();
        cout<<&str[0]<<endl;
    }catch(std::exception& e){
        cout<<e.what()<<endl;
    }
}

receive()和read_some()的error_code`重载形式

std::size_t receive(const MutableBufferSequence& buffers, socket_base::message_flags flags, error_code& ec);
std::size_t read_some(const MutableBufferSequence& buffers, error_code& ec);

异步通信

服务器端

class server{
public:
    typedef server this_type;
    typedef ip::tcp::acceptor acceptor_type;
    typedef ip::tcp::endpoint endpoint_type;
    typedef ip::tcp::socket socket_type;
    typedef shared_ptr<socket_type> socket_ptr;
public:
    server():m_acceptor(m_io, endpoint_type(ip::tcp::v4(),6688)){
        accept();
    }
    void run(){
        m_io.run();
    }
    void accept(){
        socket_ptr sock(new socket_type(m_io));
        acceptor.async_accept(
            *sock, // 异步监听服务
            bind(&this_type::accept_handler, this, boost::asio::placeholders::error, sock)
        );
    }

    // 当由TCP连接时,server::accept_handler() 被调用,使用socket对象发送数据
    void accept_handler(const error_code& ec, socket_ptr sock){
        if(ec){
            return;
        }
        cout<<"client:";
        cout<<sock->remote_endpoint().address()<< endl;
        // sock->async_write_some(buffer("hello asio"), boost::asio::placeholders::error)
        sock->async_write_some(buffer("hello asio"), bind(&this_type::write_handler, this, boost::asio::placeholders::error));
        // 再次启动异步,接收连接
        accept();
    }
    void write_handler(const system::error_code&){
        cout<<"send msg complete."<<endl;
    }
    // 也可以使用带字节数的handler形式
    void write_handler2(const error_code&, std::size_t n){
        cout<<"send msg: "<<n<<endl;
    }
private:
    io_service m_io;
    acceptor_type m_acceptor;
};

int main(){
try{
    cout<<"server start..."<<endl;
    server srv;
    srv.run();
}catch(std::exception& e){
    cout<<e.what()<<endl;
}
}

客户端

class client{
public:
    typedef client this_type;
    typedef ip::address address_type;
    typedef ip::tcp::endpoint endpoint_type;
    typedef ip::tcp::socket socket_type;
    typedef shared_ptr<socket_type> socket_ptr;
    typedef vector<char> buffer_type;
public:
    client():m_buf(100,0),m_ep(address_type::from_string("127.0.0.1"),6688){
        start();
    }
    void run(){
        m_io.run();
    }
    void start(){
        socket_ptr sock(new socket_type(m_io));
        sock->async_connect(
            m_ep,
            bind(&this_type::conn_hander, this, boost::asio::placeholders::error, sock));
    }
    void conn_handler(const error_code& ec, socket_ptr sock){
        if(ec){
            return;
        }
        cout<<"receive from: "<<sock->remote_endpoint().address();
        sock->async_read_some(
            buffer(m_buf),
            bind(&client::read_handler, this, boost::asio::placeholders::error));
    }
    void read_handler(const error_code& ec){
        if(ec){ return; }
        cout<< &m_buf[0]<<endl;
        // 若考虑缓冲区空间不足,则需要再次异步读取数据
        /*
        sock->async_read_some(
            buffer(m_buf),
            bind(&client::read_handler, this, boost::asio::placeholders::error)
        );
        */
    }
private:
    io_service m_io;
    buffer_type m_buf;
    endpoint_type m_ep;
};

int main(){
try{
    cout<<"client start..."<<endl;
    client c;
    c.run();
}catch(std::exception& e){
    cout<<e.what()<<endl;
}
}

使用lambda

void accept(){
    socket_ptr sock(new socket_type(m_io));
    m_acceptor.async_accept(
        *sock,
        [this,sock](const error_code&){
            if(ec){ return; }
            sock->async_send(
                buffer("hello asio"),
                [](const error_code&, std::size_t){
                    cout<<"send msg complete."<<endl;
                }
            );
            accpet();
        }
    );
}

客户端改用lambda表达式

void start(){
    socket_ptr cosk(new socket_type(m_io));
    sock->async_connect(
        m_io,
        [this,sock](const error_code& ec){
            if(ec){ return; }
            sock->async_read_some(
                buffer(m_buf),
                [this,sock](const error_code& ec, std::size_t){
                    read_handler(ec,sock);
                }
            );
        }
    );
}

域名解析

resolver类对应Socket API的getaddrinfo()系列函数,用于解析域名获取可用的IP地址,解析得到的IP地址
大多数情况,只知道域名,而不知道IP地址,此时需要resolver类通过域名获取可用的IP地址,实现与IP版本无关的网站解析

使用协程

协程coroutine,即泛化的例程routine
例程只有一个入口和多个出口,如C++中的函数
协程有多个入口和多个出口,从最开始入口进入后可以随时调用yeild返回,之后使用协程就会从刚才返回的地方继续执行

在boost1.53版本引入协程库boost.coroutine后,asio库提供了比异步回调handler更容易理解的协程功能
asio库同时支持stackless和stackful两种形式的协程,但stackful协程更容易使用

使用协程需要包含头文件<boost/asio/spawn.hpp>,并链接libboost_context.alibboost_coroutine.alibboost_thread.a

类原型

template<typename Handler>
class basic_yield_context{
public:
    basic_yield_context(
        const detail::weak_ptr<callee_type>& coro,
        caller_type& ca, Handler& handler
    );
    basic_yield_context operator[](boost::system::error_code& ec)const;

    detail::weak_ptr<callee_type> coro_; // 协程
    caller_type& ca_;
    Handler& handler_;
    boost::system::error_code* ec_;

    typedef basic_yield_context<
        detail::wrapped_handler<
            io_service::strand,
            void(*)(),
            detail::is_continuation_if_running>> yeild_context;
};

yield_context保存了协程的运行环境,交替执行主协程和从协程,以完成异步
operator[]用于外部获取发生的错误码,若不使用则抛出system_error异常

通常不直接创建yield_context对象,而是使用函数spawn()创建
spawn()的重载

void spawn(strand s, Function function);
void spawn(io_service io, Function function);

funtion必须符合

void func(boost::asio::yield_context yield);

用法
在asio的协程用法里,yield_context取代了handler回调
async_xxx()不需要再写回调函数,而是使用yield_context对象
async_xxx()调用后会自动交出控制权,执行其他协程,直到异步操作完成才会返回继续执行后的操作
异步读写操作的字节数可以从async_xxx()的返回值获取
整个程序的流程很像同步模式,但因为有了协程,所以是异步且高效的

int main(){
    typedef ip::tcp::acceptor acceptor_type;
    typedef ip::tcp::endpoint endpoint_type;
    typedef ip::tcp::socket socket_type;

    io_service io;
    spawn(
        io,
        [&](yield_context yield){
            acceptor_type acceptor(io, endpoint_type(ip::tcp::v4(), 6688));
            while(true){
                socket_type sock(io);
                error_code ec;
                acceptor.async_accept(sock, yield[ec]); // 使用协程,无handler

                if(ec){ return; }
                auto len= sock.async_write_some(buffer("hello coroutine"), yield); // 使用协程,无handler
                cout<<"send:"<<len<<" bytes"<<endl;
            }
        }
    );
    io.run(); // 启动事件循环
}

接收连接和发送数据使用的则是异步的async_accept()/async_write_some()
这样在每个异步调用处不会发生阻塞,而是转入其他协程,只有真正发生连接事件和写完成事件时才会继续执行后面的代码

server

#include "boost/asio.hpp"
#include "boost/shared_ptr.hpp"
#include "boost/thread.hpp"

using namespace std;
using namespace boost::asio;

#ifdef _MSC_VER
#define _WIN32_WINNT 0X0501  // 避免VC下编译警告
#endif

#define PORT 9001
#define IPV6
// #define IPV4
class AsyncServer {
public:
    // 构造函数
    AsyncServer(io_service &io, ip::tcp::endpoint &ep) : ios(io), acceptor(io, ep) {
        // acceptor(ios,ep);
        start();
    }

    // 启动异步接受客户端连接
    void start() {
        sock_ptr sock(new ip::tcp::socket(ios));
        // 当有连接进入时回调accept_handler函数
        acceptor.async_accept(
            *sock,
            boost::bind(
                &AsyncServer::accept_handler, this,
                boost::asio::placeholders::error, sock));
    }

private:
    // io_service &ios;
    io_context &ios;
    ip::tcp::acceptor acceptor;
    typedef boost::shared_ptr<ip::tcp::socket> sock_ptr;

    void accept_handler(const boost::system::error_code &ec, sock_ptr sock) {
        if (ec)
            return;
        // 输出客户端连接信息
        std::cout << "remote ip: " << sock->remote_endpoint().address() << endl;
        std::cout << "remote port: " << sock->remote_endpoint().port() << std::endl;
        // 异步向客户端发送数据,发送完成时调用write_handler
        sock->async_write_some(
            buffer("I heard you!\n"),
            bind(&AsyncServer::write_handler, this, boost::asio::placeholders::error));

        // 再次启动异步接受连接
        start();
    }

    void write_handler(const boost::system::error_code &) {
        cout << "send msg complete!" << endl;
    }
};

// async
int asynchronous() {
    try {
        // 定义 io_service 对象
        io_context ios;
        // io_service ios;
        // 定义服务端 endpoint 对象(协议和监听端口)
#ifdef IPV4
        ip::tcp::endpoint ep(ip::tcp::v4(), PORT);
#endif

#ifdef IPV6
        ip::tcp::endpoint ep(ip::tcp::v6(), PORT);
#endif
        // 启动异步服务
        AsyncServer server(ios, ep);
        // 等待异步完成
        ios.run();
    } catch (std::exception &e) {
        cout << e.what() << endl;
    }
    return 0;
}

// sync
void synchronous() {
    // 所有 asio 类都需要 io_service 对象
    io_service ioserv;
    ip::tcp::acceptor acceptor(ioserv, ip::tcp::endpoint(ip::tcp::v4(), 9001));

    while (true) {
        ip::tcp::socket socket(ioserv);
        // 等待客户端连接
        acceptor.accept(socket);
        // 显示连接的客户端
        std::cout << socket.remote_endpoint().address() << std::endl;
        // 向客户端发送消息
        boost::system::error_code ec;
        socket.write_some(buffer("hello, I'll serve for U!"), ec);
        // 若出错
        if (ec) {
            std::cout << boost::system::system_error(ec).what() << std::endl;
            break;
        }
        // 等待下一客户端连接
    }
}

int main(int argc, char *argv[]) {
    // synchronous();
    asynchronous();

    return 0;
}

client

#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <iostream>
using namespace std;
using namespace boost::asio;
// using boost::asio;

int main(int argc, char *argv[]) {
    try {
        // tcp client
        boost::asio::io_service io;
        ip::tcp::socket socket(io);
        // connect to server
        ip::tcp::endpoint ep(ip::address::from_string("127.0.0.1"), 9001);
        socket.connect(ep);
        while (true) {
            boost::array<char, 128> buf;
            boost::system::error_code ec;
            // read from socket
            size_t len = socket.read_some(boost::asio::buffer(buf), ec);
            if (ec == boost::asio::error::eof) {
                break;
            } else if (ec) {
                throw boost::system::system_error(ec);
            }
            cout.write(buf.data(), len);
        }
    } catch (std::exception &e) {
        std::cout << e.what() << std::endl;
    }
}
posted @ 2024-11-01 19:58  sgqmax  阅读(4)  评论(0编辑  收藏  举报