Boost Asio库学习记录

Boost.Asio

参考:https://www.cnblogs.com/hanerfan/p/5161955.html
https://blog.csdn.net/rain_qingtian/article/details/39003597

Boost.Asio是一个跨平台的C++库,用于网络和底层I/O编程,可以在I/O对象(如socket)上执行同步和异步操作。

在socket连接操作中:
你的程序中需要至少定义一个io_context对象:boost::asio::io_context io_context。io_context表示程序到操作系统I/O服务的“连接”。

为执行I/O操作,还需要一个I/O对象(通常需要使用io_context构造),如一个TCP套接字:boost::asio::ip::tcp::socket socket(io_context)。

1)同步的连接过程中,发生以下事件序列(对应下面的左图):

(1)程序通过I/O对象启动连接操作:socket.connect(server_endpoint);

(2)I/O对象将请求转发给io_context;

(3)io_context请求操作系统去执行连接操作;

(4)操作系统将操作结果返回给io_context;

(5)io_context将操作的(错误)结果转换成boost::system::error_code对象,并回传给I/O对象;

(6)如果操作失败,I/O对象抛出boost::system::system_error异常。

如果是使用以下方式,则只设置错误码,不会抛出异常:

boost::system::error_code ec;
socket.connect(server_endpoint, ec);

2)异步的连接过程中,发生以下事件序列(对应下面的中图和右图):

(1)程序通过I/O对象启动连接操作:

socket.async_connect(server_endpoint, your_completion_handler);

your_completion_handler是一个函数(对象),原型:void your_completion_handler(const boost::system::error_code& ec);

(2)I/O对象将请求转发给io_context;

(3)io_context发信号给操作系统,告知它去开始一个异步的连接操作;

一段时间过去… …注意,在同步的情形下,程序会一直等待连接操作完成,而异步则是先立即返回。

(4)连接操作完成时,操作系统把结果放在队列中;

(5)程序必须调用io_context::run()(或类似函数)以取得操作结果。一般在你刚启动第一个异步操作时就要调用run();

io_context对象未停止(stopped()返回false)且还有未完成的操作时,run()会一直阻塞,否则直接返回。

我的理解(io_context对象未停止时):如果当前有未完成的异步操作且队列为空,则需要等待,因此run()将阻塞(在Linux下借助pstack可知是阻塞于epoll_wait()或pthread_cond_wait()等)。操作系统完成某个异步操作后,把结果放到队列并通知应用程序。run()被“唤醒”,从队列中取出结果并调用相应的回调函数;如果当前没有未完成的异步操作且队列为空,表示所有异步操作已经完成,则run()将直接返回;当然,如果当前队列非空,则run()直接取出结果并调用回调函数。

asio保证了回调函数只会被run()所在线程调用。因此,若没有run(),回调函数永远不会被调用。

(6)在run()中io_service将操作结果取出队列并翻译成error_code,然后传递给your_completion_handler。
在这里插入图片描述
I/O服务:

work类
run() vs poll()
stop()
post() vs dispatch()
buffer类
缓冲区管理

I/O对象:

socket
信号处理
定时器
strand

work类

work类用于通知io_context是否可以结束,只要对象work(io_context)存在,io_context就不会结束。所以work类用起来更像是一个标识,比如:

boost::asio::io_context io_context;
boost::asio::io_context::work* work = new boost::asio::io_context::work( io_context );
// delete work; // 如果不注释掉这一句,则run loop不会退出;一般用shared_ptr维护work对象,使用work.reset()来结束其生命周期。
io_service.run()

post() vs dispatch()

post()和dispatch()都是要求io_service执行一个handler,但是dispatch()要求立即执行,而post()总是先把该handler加入事件队列。

什么时候需要使用post()?当不希望立即调用一个handler,而是异步调用该handler,则应该调用post()把该handler交由io_service放到事件队列里去执行。比如,Boost.Asio自带的聊天室示例,其中实现了一个支持异步IO的聊天室客户端,是个很好的例子。

chat_client.cpp 的write()函数之所以要使用post(),是为了避免临界区同步问题。write()调用和do_write()里async_write()的执行分别属于两个线程,前者会往write_msgs_里写数据,而后者会从write_msgs_里读数据,如果不使用post(),而直接调用do_write(),显然需要使用锁来同步write_msgs_。但是使用post()相当于由io_service来调度write_msgs_的读写,这就在一个线程内完成,无需额外的锁机制。

boost::asio::error

一般而言我们创建用于接收error的类型大多声明如下:

boost::system::error_code error  

我们用这个类型去接受在函数中产生的错误, 如:

socket.connect(endpoint, error);  

如果连接失败,错误类型会保存到error中,比如连接主机失败可能会返回这样的错误

boost::asio::error::host_not_found;  

通过if(error)检测到error后,抛出异常

 throw boost::system::system_error(error);  

需要注意的是,我们的error被转化成 system_error了,显示错误很简单了,std::cout << e.what()

同步:
当然了boost::system::error_code error还用有用的,同步调用的时候我们就用它作为参数。如:

boost::system::error_code error;  
size_t len = socket.read_some(boost::asio::buffer(buf), error);  

同样在异步调用的回调handle中也用它作参数如:

 void handle_write(const boost::system::error_code& /*error*/,  
                  size_t /*bytes_transferred*/)  
{  
}  

异步:

产生的异常error的传递是个问题,因为异步会立刻返回,局部变量是会被销毁的。而boost::asio::placeholders::error, 将会保存异常的状态,这样我们使用异步调用时如
socket::async_write_some的时候不用自己创建boost::system::error_code error了,直接使用boost::asio::placeholders::error作为参数即可,
同理,我们sync_write_some需要返回读写数据的大小,令人开心的是boost::asio::placeholders::bytes_transferred直接作为参数就可以保存数据大小。

 boost::asio::async_write(socket_,  
                         boost::asio::buffer(message_),  
                         boost::bind(&tcp_connection::handle_write, shared_from_this(),  
                         boost::asio::placeholders::error,  
                         boost::asio::placeholders::bytes_transferred));  

参考手册上说的很明确,下面两个类就是为异步调用使用bind的时候设计的

 boost::asio::placeholders::error  
boost::asio::placeholders::bytes_transferred  

error总结

异步就用 boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred 同步就用
boost::system::error_code

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值