boost::asio 连接管理11 如何关闭连接
2014-06-20 14:33
429 查看
在实际产品运行中,对连接管理有了更新的认识,这里分享一下。
中的Client对象:
[cpp] view plaincopyprint?
#include "core/connection.h"
#include <vector>
using namespace std;
class Client: public Connection<Client> {
public:
Client(io_service& s);
1. 如果你不再发起任何异步读/写操作
因为每一次异步读/写操作都会将Client对象自己的this指针包装成shared_ptr,通过bind交给boost asio框架,此时框架将持有该shared_ptr,直到读/写完成,回调我们自己的函数后才会将引用计数器减1。
2. 如果没有任何其他对象或者容器持有这个shared_ptr。
实际通信程序为了实现服务器事件通知,我会将所有的Client对象的shared_ptr保存在一个容器中,比如map。然后定期的检查这些Client有没有在数据库中有事件要发布,如果有,则调用Client的方法发送数据。也会定期检查Client对象代表的连接上的心跳消息,如果心跳超时,则将share_ptr从map中移除掉,并停止任何读/写的异步操作(也就是上上面第一个条件)
只有在满足了这两个条件的情况下,shared_ptr会自动销毁Client对象,Client对象内部的成员变量socket也会被销毁。
[cpp] view plaincopyprint?
void CloseSocket() {
try {
socket.shutdown(tcp::socket::shutdown_both);
socket.close();
} catch (std::exception& e) {
BOOSTER_INFO("Connection") << "thread id: " << this_thread::get_id() << e.what() << endl;
}
}
参考:http://web.archiveorange.com/archive/v/Q0J4VefPMc2v8QYvcVr3
[cpp] view plaincopyprint?
void Client::ToClose() {
strand_.post(bind(&Client::DoClose, shared_from_this()));
}
void Client::DoClose() {
CloseSocket();
}
其他线程就只需要调用ToClose函数即可。
由于strand_.post的桥梁作用,CloseSocket会在io_service运行的线程池中被保护。就不会出现和async_read/async_write同时被执行的情况。
在实际编程中我们可能由于很多原因要关闭连接,比如前面说的心跳超时,也有收到了不正确的数据,或者在asio传递出来的网络错误,又或者是收到了关闭进程的信号。
无论何种原因,都可以采用上面的方式进行关闭连接。所以原则只有一个,记住shared_ptr的生命周期即可。
1. 线程池中运行这io_service对象,进行异步的读/写操作
2. 一个线程定期检查数据库中的事件消息,并发送给所有远程终端,同时检查每个连接的心跳超时时间。
3. 一个全局的map对象保存了所有Client的shared_ptr。
在以后的文章中会探索。
shared_ptr管理连接对象的生命周期
shared_ptr的引用计数器决定了连接对象的生命周期。这里我说的连接对象就是在我的前文:/article/1664195.html中的Client对象:
[cpp] view plaincopyprint?
#include "core/connection.h"
#include <vector>
using namespace std;
class Client: public Connection<Client> {
public:
Client(io_service& s);
销毁连接对象的条件
那么什么情况下shared_ptr的引用技术会变成0呢?必须满足下列所有条件:1. 如果你不再发起任何异步读/写操作
因为每一次异步读/写操作都会将Client对象自己的this指针包装成shared_ptr,通过bind交给boost asio框架,此时框架将持有该shared_ptr,直到读/写完成,回调我们自己的函数后才会将引用计数器减1。
2. 如果没有任何其他对象或者容器持有这个shared_ptr。
实际通信程序为了实现服务器事件通知,我会将所有的Client对象的shared_ptr保存在一个容器中,比如map。然后定期的检查这些Client有没有在数据库中有事件要发布,如果有,则调用Client的方法发送数据。也会定期检查Client对象代表的连接上的心跳消息,如果心跳超时,则将share_ptr从map中移除掉,并停止任何读/写的异步操作(也就是上上面第一个条件)
只有在满足了这两个条件的情况下,shared_ptr会自动销毁Client对象,Client对象内部的成员变量socket也会被销毁。
一般情况下不需要手动关闭socket
所以,我之前用下面的函数关闭socket一般情况下是不需要的[cpp] view plaincopyprint?
void CloseSocket() {
try {
socket.shutdown(tcp::socket::shutdown_both);
socket.close();
} catch (std::exception& e) {
BOOSTER_INFO("Connection") << "thread id: " << this_thread::get_id() << e.what() << endl;
}
}
多线程环境下关闭连接导致crash
如果要使用CloseSocket,多线程环境下必须小心。比如我碰到过这样的情况:一个线程检查到了超时,然后调用Client::CloseSocket方法,同时另一个线程正在该Client上发起了异步的读/写操作,结果进程崩溃了。boost asio不允许这样做。参考:http://web.archiveorange.com/archive/v/Q0J4VefPMc2v8QYvcVr3
异步读/写阻塞导致连接对象无法销毁
如果就坚持不用CloseSocket行么,我碰到另一种情况,async_write/async_read会阻塞,结果shared_ptr的引用计数不会为0,所以Client对象无法被销毁。那么怎么安全的调用CloseSocket的呢?用strand,下面是代码:[cpp] view plaincopyprint?
void Client::ToClose() {
strand_.post(bind(&Client::DoClose, shared_from_this()));
}
void Client::DoClose() {
CloseSocket();
}
其他线程就只需要调用ToClose函数即可。
由于strand_.post的桥梁作用,CloseSocket会在io_service运行的线程池中被保护。就不会出现和async_read/async_write同时被执行的情况。
在实际编程中我们可能由于很多原因要关闭连接,比如前面说的心跳超时,也有收到了不正确的数据,或者在asio传递出来的网络错误,又或者是收到了关闭进程的信号。
无论何种原因,都可以采用上面的方式进行关闭连接。所以原则只有一个,记住shared_ptr的生命周期即可。
优雅的关闭进程
如何在这种情况下优雅的退出进程是个问题,否则退出时程序会crash,留下core文件。这种情况是:1. 线程池中运行这io_service对象,进行异步的读/写操作
2. 一个线程定期检查数据库中的事件消息,并发送给所有远程终端,同时检查每个连接的心跳超时时间。
3. 一个全局的map对象保存了所有Client的shared_ptr。
在以后的文章中会探索。
相关文章推荐
- boost::asio 连接管理11 如何关闭连接
- boost::asio 连接管理3
- boost::asio 连接管理8
- boost::asio 连接管理4
- boost::asio 连接管理6
- boost::asio 连接管理5
- boost::asio 连接管理7
- boost::asio 连接管理9
- boost::asio 连接管理8
- boost::asio 连接管理10
- boost::asio 连接管理1
- boost::asio 连接管理2
- boost::asio::ip::tcp::socket is connected?(如何知道socket已经连接?)
- boost::asio::ip::tcp::socket is connected?(如何知道socket已经连接?)
- 如何在OPhone 1.0和1.5中打开和管理数据连接
- 如何开发一个学生成绩管理糸统(11)
- 如何关闭WCF连接
- 如何解决由于启动用户实例的进程时出错,导致无法生成 SQL Server 的用户实例。该连接将关闭。
- 一个通用的单元测试框架的思考和设计06-实现篇-自动管理测试数据-如何临时关闭数据外键约束
- 使用 boost::signals2 的智能连接管理