boost:asio 联接管理1
在完成了第一个基于boost::asio的通信服务程序后,回顾一下所用到的概念,参考一些资料。将用一个系列来归纳一下如何通过boost::asio编写高性能TCP服务程序。
本篇从简单的单线程开始,描述如何监听端口,接收连接请求。同时也复用了前面的“优雅的退出” 的代码。
首先main函数创建一个server对象,server对象负责监听本地8888端口,一旦有连接请求,则创建一个connection对象,并调用StartWork开始工作。
下面是代码:
#include <cstdlib> #include <boost/asio.hpp> #include <boost/bind.hpp> #include <iostream> using namespace boost; using namespace boost::asio; using ip::tcp; using namespace std; using boost::system::error_code; class Connection { public: Connection(io_service& s): socket(s) { } ~Connection() { socket.close(); cout << "~Connection" << endl; } void StartWork() { cout << "The new connection object is starting now." << endl; } public: tcp::socket socket; }; class Server { public: Server(io_service & s, tcp::endpoint const& listen_endpoint) : io_(s), signals_(s), acceptor_(s, listen_endpoint) { signals_.add(SIGINT); signals_.add(SIGTERM); #if defined(SIGQUIT) signals_.add(SIGQUIT); #endif signals_.async_wait(boost::bind(&Server::Stop, this)); shared_ptr<Connection> c(new Connection(io_)); cout << "count1:" << c.use_count() << endl; acceptor_.async_accept(c->socket, boost::bind(&Server::AfterAccept, this, c, _1)); cout << "count2:" << c.use_count() << endl; } void Run() { io_.run(); } void AfterAccept(shared_ptr<Connection>& c, error_code const& ec) { // Check whether the server was stopped by a signal before this completion // handler had a chance to run. if (!acceptor_.is_open()) { return; } cout << "count3:" << c.use_count() << endl; if (!ec) { c->StartWork(); shared_ptr<Connection> c2(new Connection(io_)); acceptor_.async_accept(c2->socket, boost::bind(&Server::AfterAccept, this, c2, _1)); } } private: void Stop() { cout << "stop io_service" << endl; io_.stop(); } private: io_service& io_; boost::asio::signal_set signals_; tcp::acceptor acceptor_; }; int main(int argc, char** argv) { io_service s; tcp::endpoint listen_endpoint(tcp::v4(), 8888); Server server(s, listen_endpoint); server.Run(); return 0; }
代码有点长,但是和真正的产品来讲已经很简单了。
通过telnet localhost 8888 命令可以迅速测试一下,会看到连接已经建立,很快又被断开。
程序打印出两行消息:
The new connection object is starting now. ~Connection
这个关系到对象生命周期的问题。
先看Server的构造函数里面,创建了一个Connection对象,用shared_ptr管理,此时引用计数为1,然后在下面一句bind调用时,将c变量保存到bind_t中了,因此引用计数会增加到2, 之后构造函数结束,引用计数减到1.
asio回调bind_t对象的operator()() 的时候最终会调用到AfterAccept函数,c对象作为引用参数传递进来,因此参数传递没有增加引用计数。但是这中间的asio回调机制使得引用计数增加到2.
然后里面运行了StartWork,StartWork很快返回。当AfterAccept函数退出时,asio的回调机制的清理将引用计数减到1, 而bind_t这个function object对象也被销毁,因此引用计数降到0,所以c的析构函数被调用。
bind内部的细节不去管他,这里只要注意一旦StartWork函数退出,对象就会被销毁。
由于在AfterAccept函数最后总是又调用一次async_accept函数,所以会一直不断的等待新的连接。直到io_service::stop函数被调用,才结束整个程序。
这个简单的例子是一个开始,也说明了连接是如何建立的,连接对象的生命周期是如此脆弱。后面会继续改进。