这节先重构下代码,业务逻辑代码和通信基础设施代码需要解耦。业务逻辑代码处理通信过程中的协议,以及背后需要的应用逻辑。而通信基础设施代码专注于并发,TCP连接等特性。
首先把前面的代码Connection的一些成员函数纯虚函数,业务类可以继承之,根据需要重写虚函数。
Server类编程模板类,接受业务类作为模板参数,只要该类提供几个必须的公有成员函数即可。
因此目录结构调整如下:
├── CMakeLists.txt ├── include │ ├── business │ └── core └── src ├── business ├── CMakeLists.txt ├── core └── main.cc
connection类也放在core目录下,也变成模板类。core下的这两个类就是通信基础设施类。
新建一个business类,里面都放应用逻辑相关的代码。现在这里有一个Client类。
对应的src/CMakeLists.txt文件适当修改:
cmake_minimum_required(VERSION 2.8) set(CMAKE_BUILD_TYPE Debug) set(PROJECT_INCLUDE_DIR ../include) find_package(Boost COMPONENTS system filesystem thread REQUIRED) include_directories(${Boost_INCLUDE_DIR} ${PROJECT_INCLUDE_DIR}) AUX_SOURCE_DIRECTORY(${CMAKE_SOURCE_DIR}/src CPP_LIST1) AUX_SOURCE_DIRECTORY(${CMAKE_SOURCE_DIR}/src/core CPP_LIST2) AUX_SOURCE_DIRECTORY(${CMAKE_SOURCE_DIR}/src/business CPP_LIST3) add_executable(service ${CPP_LIST1} ${CPP_LIST2} ${CPP_LIST3}) target_link_libraries(service ${Boost_LIBRARIES}) add_definitions(-Wall)
#include <iostream> #include "core/server.h" #include "business/client.h" using namespace std; int main(int argc,char ** argv) { try { io_service iosev; tcp::endpoint listen_endpoint(tcp::v4(), 8888); Server<Client> server(iosev, listen_endpoint, 10); server.Run(); } catch(std::exception const& ex) { cout << "Exception: " << ex.what() << ""; } }
看一下core/server.h文件:
#ifndef CORE_SERVER_H_ #define CORE_SERVER_H_ #include <boost/asio.hpp> #include <boost/bind.hpp> #include <boost/thread/thread.hpp> #include <vector> using namespace std; using namespace boost; using boost::system::error_code; using namespace boost::asio; using ip::tcp; // Crate a thread pool for io_service. // Run the io_service to accept new incoming TCP connection and handle the I/O events // You should provide your class as template argument here // Your class must inherit from Connection class. template<class T> class Server { public: typedef T ClientType; Server(io_service& s, tcp::endpoint const& listen_endpoint, size_t threads_number) : io_(s), signals_(s), acceptor_(io_, listen_endpoint), thread_pool_size_(threads_number) { signals_.add(SIGINT); signals_.add(SIGTERM); #if defined(SIGQUIT) signals_.add(SIGQUIT); #endif signals_.async_wait(bind(&Server::Stop, this)); shared_ptr<ClientType> c(new ClientType(io_)); acceptor_.async_accept(c->socket, bind(&Server::AfterAccept, this, c, _1)); } void AfterAccept(shared_ptr<ClientType>& 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()) { cout << "acceptor is closed" << endl; return; } if (!ec) { c->StartJob(); shared_ptr<ClientType> c2(new ClientType(io_)); acceptor_.async_accept(c2->socket, bind(&Server::AfterAccept, this, c2, _1)); } } // Create a thread pool for io_service // Launch io_service void Run() { // Create a pool of threads to run all of the io_services. vector<shared_ptr<thread> > threads; for (size_t i = 0; i < thread_pool_size_; ++i) { shared_ptr<thread> t(new thread(bind(&io_service::run, &io_))); threads.push_back(t); } // Wait for all threads in the pool to exit. for (std::size_t i = 0; i < threads.size(); ++i) { threads[i]->join(); } } private: void Stop() { cout << "stopping" << endl; acceptor_.close(); io_.stop(); } private: io_service& io_; boost::asio::signal_set signals_; tcp::acceptor acceptor_; size_t thread_pool_size_; }; #endif
改动有两处:#ifndef CORE_CONNECTION_H_ #define CORE_CONNECTION_H_ #include <boost/asio.hpp> #include <boost/enable_shared_from_this.hpp> using namespace boost::asio; using ip::tcp; using boost::system::error_code; using namespace boost; using namespace std; template<class T> class Connection: public boost::enable_shared_from_this<T> { public: Connection(io_service& s) : socket(s), strand_(s) { } ~Connection() { } // You must override it yourself // Default implementation closes the socket using shutdonw&cloes methods // You could override it if want change it // Or resue it with Connection::CloseSocket() format void CloseSocket() { socket.shutdown(tcp::socket::shutdown_both); socket.close(); } // You must override it yourself virtual void StartJob() = 0; tcp::socket socket; // Strand to ensure the connection's handlers are not called concurrently. boost::asio::io_service::strand strand_; }; #endif
1.加了关键字virtual ... =0
2. enable_shared_from_this的模板参数是T而不再是Connection类。
现在看看business/client.h,这个类实现了具体服务器和一个客户端之间通信的逻辑。
#ifndef BUSINESS_CLIENT_H_ #define BUSINESS_CLIENT_H_ #include "core/connection.h" #include <vector> using namespace std; class Client: public Connection<Client> { public: Client(io_service& s); void StartJob(); void CloseSocket(); void AfterReadChar(error_code const& ec); private: vector<char> read_buffer_; }; #endif
#include "business/client.h" #include <boost/bind.hpp> using namespace boost; Client::Client(io_service& s): Connection(s), read_buffer_(1, 0) { } void Client::StartJob() { async_read(socket, buffer(read_buffer_), strand_.wrap(bind(&Client::AfterReadChar, shared_from_this(), _1))); } void Client::CloseSocket() { Connection::CloseSocket(); } void Client::AfterReadChar(error_code const& ec) { if (ec) { cout << ec.message() << endl; return; } char x = read_buffer_[0]; if (x == 'a') { cout << "correct data received" << endl; async_read(socket, buffer(read_buffer_), strand_.wrap(bind(&Client::AfterReadChar, shared_from_this(), _1))); } else { cout << "wrong data received, char is:" << (int) x << endl; CloseSocket(); } }