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库基于前摄器模式封装了操作系统的select
、poll
、epoll
、kqueue
等机制,实现了异步IO模型
核心类是io_service
,相当于前摄器模式中的proactor
在同步模式下,程序发起一个IO操作,向io_service
提交请求,io_service
将操作交给操作系统,同步等待
当IO操作完成时,操作系统通知io_service
,然后io_service
再把结果返回给程序,完成同步流程
在异步模式下,程序除了发起IO操作,还定义一个完成处理回调,io_service
将IO操作交给操作系统执行,立即返回
调用io_service
的run()
成员函数等待异步操作完成,io_service
向操作系统获取结果,再调用回调
handler
是符合某种函数签名的回调函数,handler必须是可拷贝的,因为io_service
会存储handler的拷贝
asio库中,handler有以下三种:
- 只有一个
error_code
参数
标志某个异步事件完成 - 有
error_code
和signal_number
两个参数
标志发生一个UNIX信号事件 - 有
error_code
和bytes_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_code
和system_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.a
、libboost_coroutine.a
和libboost_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;
}
}