├── .gitignore ├── CMakeLists.txt ├── README.md ├── common.h ├── test-mcmt.cpp ├── test-otpc-conn.cpp ├── test-otpc-conn.hpp ├── test-otpc-tp.cpp └── test-otpc.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | build 3 | Debug 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | PROJECT(boost-asio-examples) 3 | 4 | # Usage: 5 | # cmake .. -DCMAKE_INCLUDE_PATH=/home/ott/exp/include -DCMAKE_LIBRARY_PATH=/home/ott/exp/lib 6 | # -DBOOST_INCLUDEDIR=/home/ott/exp/include -DBOOST_LIBRARYDIR=/home/ott/exp/lib 7 | # 8 | 9 | SET(Boost_USE_STATIC_LIBS OFF) 10 | SET(Boost_USE_MULTITHREAD ON) 11 | FIND_PACKAGE(Boost 1.42.0 REQUIRED COMPONENTS system thread regex) 12 | IF(Boost_FOUND) 13 | INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS}) 14 | LINK_DIRECTORIES(${Boost_LIBRARY_DIRS}) 15 | ENDIF(Boost_FOUND) 16 | 17 | SET(USED_LIBS ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${Boost_REGEX_LIBRARY}) 18 | 19 | ADD_EXECUTABLE(test-otpc test-otpc.cpp test-otpc-conn.cpp) 20 | TARGET_LINK_LIBRARIES(test-otpc ${USED_LIBS}) 21 | 22 | ADD_EXECUTABLE(test-mcmt test-mcmt.cpp) 23 | TARGET_LINK_LIBRARIES(test-mcmt ${USED_LIBS}) 24 | 25 | #ADD_EXECUTABLE(test-otpc-tp test-otpc-tp.cpp test-otpc-conn.cpp) 26 | #TARGET_LINK_LIBRARIES(test-otpc-tp ${USED_LIBS}) 27 | 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | boost-asio-examples 2 | =================== 3 | 4 | Source code for examples from article [What is Boost.Asio, and why we should use it](http://alexott.net/en/cpp/BoostAsioNotes.html). Examples are provided as-is, and just for illustration. **Repository isn't maintained and left just for illustration!** 5 | 6 | Source code requires Boost >= 1.42 & CMake >= 2.6. Use following commands to configure & build examples: 7 | 8 | mkdir build 9 | cd build 10 | cmake .. 11 | make 12 | 13 | 14 | This work is licensed under a Creative Commons Attribution 3.0 Unported License. 15 | -------------------------------------------------------------------------------- /common.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file common.h 3 | * @author Alex Ott 4 | * 5 | * @brief Declarations and includes, common for all boost::asio based programs 6 | * 7 | * 8 | */ 9 | 10 | #ifndef _COMMON_H 11 | #define _COMMON_H 1 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | namespace ba=boost::asio; 30 | namespace bs=boost::system; 31 | 32 | typedef boost::shared_ptr socket_ptr; 33 | typedef boost::shared_ptr io_service_ptr; 34 | 35 | #endif /* _COMMON_H */ 36 | 37 | -------------------------------------------------------------------------------- /test-mcmt.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file test-mcmt.cpp 3 | * @author Alex Ott 4 | * 5 | * @brief Impelements many threads - many connections strategy of connections handling. 6 | * All input/output is async. Server use several io_service objects to scale to work on 7 | * multiprocessor/multicore systems 8 | * 9 | * 10 | */ 11 | 12 | #define BOOST_ASIO_DISABLE_KQUEUE 1 13 | #include "common.h" 14 | #include 15 | 16 | typedef std::deque ios_deque; 17 | 18 | /** 19 | * Connection class, implementing async input/output 20 | * 21 | */ 22 | class connection : public boost::enable_shared_from_this { 23 | public: 24 | typedef boost::shared_ptr pointer; 25 | 26 | /** 27 | * Create new connection 28 | * 29 | * @param io_service io_service in which this connection will work 30 | * 31 | * @return pointer to newly allocated object 32 | */ 33 | static pointer create(ba::io_service& io_service) { 34 | return pointer(new connection(io_service)); 35 | } 36 | 37 | /** 38 | * Return socket, associated with this connection. This socket used in accept operation 39 | * 40 | * 41 | * @return reference to socket 42 | */ 43 | ba::ip::tcp::socket& socket() { 44 | return socket_; 45 | } 46 | 47 | /** 48 | * Start input/output chain with reading of headers from browser 49 | * 50 | */ 51 | void start() { 52 | // start reading of headers from browser 53 | boost::asio::async_read_until(socket_, buf, boost::regex("\r\n\r\n"), 54 | boost::bind(&connection::handle_read, shared_from_this(), 55 | ba::placeholders::error, 56 | ba::placeholders::bytes_transferred)); 57 | } 58 | 59 | private: 60 | /** 61 | * Initialize connection 62 | * 63 | * @param io_service 64 | * 65 | * @return 66 | */ 67 | connection(ba::io_service& io_service) : socket_(io_service) { 68 | } 69 | 70 | /** 71 | * Called when data written to browser 72 | * 73 | * @param error object, containing information about errors 74 | * @param bytes_transferred number of transferred bytes 75 | */ 76 | void handle_write(const boost::system::error_code& error, 77 | size_t bytes_transferred) { 78 | } 79 | 80 | /** 81 | * Called when data readed from browser 82 | * 83 | * @param error object, containing information about errors 84 | * @param bytes_transferred number of transferred bytes 85 | */ 86 | void handle_read(const boost::system::error_code& error, 87 | size_t bytes_transferred) { 88 | ba::async_write(socket_, ba::buffer(message_), 89 | boost::bind(&connection::handle_write, shared_from_this(), 90 | ba::placeholders::error, 91 | ba::placeholders::bytes_transferred)); 92 | } 93 | 94 | ba::ip::tcp::socket socket_; /**< socket, associated with browser */ 95 | boost::asio::streambuf buf; /**< buffer for request data */ 96 | static std::string message_; /**< data, that we'll return to browser */ 97 | }; 98 | 99 | std::string connection::message_="HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n" 100 | "test" 101 | "

Test

This is a test!

"; 102 | 103 | 104 | /** 105 | * Server class 106 | * 107 | */ 108 | class server { 109 | public: 110 | /** 111 | * Initialize all needed data 112 | * 113 | * @param io_service reference to io_service 114 | * @param port port to listen on, by default - 10001 115 | */ 116 | server(const ios_deque& io_services, int port=10001) 117 | : io_services_(io_services), 118 | acceptor_(*io_services.front(), 119 | ba::ip::tcp::endpoint(ba::ip::tcp::v4(), port)) { 120 | start_accept(); 121 | } 122 | 123 | private: 124 | /** 125 | * start connection accepting in async mode 126 | * 127 | */ 128 | void start_accept() { 129 | // select next io_service object 130 | io_services_.push_back(io_services_.front()); 131 | io_services_.pop_front(); 132 | // create new connection 133 | connection::pointer new_connection = connection::create(*io_services_.front()); 134 | // start acceptor in async mode 135 | acceptor_.async_accept(new_connection->socket(), 136 | boost::bind(&server::handle_accept, this, new_connection, 137 | ba::placeholders::error)); 138 | } 139 | 140 | /** 141 | * Run when new connection is accepted 142 | * 143 | * @param new_connection accepted connection 144 | * @param error reference to error object 145 | */ 146 | void handle_accept(connection::pointer new_connection, 147 | const boost::system::error_code& error) { 148 | if (!error) { 149 | new_connection->start(); 150 | start_accept(); 151 | } 152 | } 153 | 154 | ios_deque io_services_; /**< deque of pointers to io_services */ 155 | ba::ip::tcp::acceptor acceptor_; /**< object, that accepts new connections */ 156 | }; 157 | 158 | /** 159 | * Main routine 160 | * 161 | * @param argc number of arguments 162 | * @param argv pointers to arguments 163 | * 164 | * @return error code 165 | */ 166 | int main(int argc, char** argv) { 167 | try { 168 | int thread_num=10; 169 | // read number of threads with io_services from command line, if provided 170 | if(argc > 1) 171 | thread_num=boost::lexical_cast(argv[1]); 172 | ios_deque io_services; 173 | 174 | boost::thread_group thr_grp; 175 | // create threads for each io_service 176 | for (int i = 0; i < thread_num; ++i) { 177 | io_service_ptr ios(new ba::io_service); 178 | io_services.push_back(ios); 179 | // run io_service in their own thread 180 | thr_grp.create_thread(boost::bind(&ba::io_service::run, ios)); 181 | } 182 | // create server 183 | server server(io_services); 184 | // wait until all thread will finished 185 | thr_grp.join_all(); 186 | } catch (std::exception& e) { 187 | std::cerr << e.what() << std::endl; 188 | } 189 | 190 | 191 | return 0; 192 | } 193 | 194 | 195 | -------------------------------------------------------------------------------- /test-otpc-conn.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file test-otpc-conn.cpp 3 | * @author Alex Ott 4 | * 5 | * @brief Implementation of sync connection class 6 | * 7 | * 8 | */ 9 | 10 | #include "test-otpc-conn.hpp" 11 | 12 | /** 13 | * What we'll return to browser/test utility 14 | * 15 | */ 16 | const std::string connection::message_="HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n" 17 | "test" 18 | "

Test

This is a test!

"; 19 | 20 | 21 | /** 22 | * Constructor for class, initilize socket for this connection 23 | * 24 | * @param io_service reference to io_service 25 | * 26 | * @return nothing 27 | */ 28 | connection::connection(ba::io_service& io_service, hide_me) : 29 | io_service_(io_service), socket_(io_service) { 30 | } 31 | 32 | 33 | /** 34 | * Perform all input/output operations in sync mode 35 | * 36 | */ 37 | void connection::run() { 38 | try { 39 | // read data from socket until empty line 40 | ba::read_until(socket_, buf, boost::regex("\r\n\r\n")); 41 | // write answer to socket 42 | ba::write(socket_,ba::buffer(message_),ba::transfer_all()); 43 | // close socket 44 | socket_.close(); 45 | } catch(std::exception& x) { 46 | // std::cerr << "Exception: " << x.what() << std::endl; 47 | } 48 | } 49 | 50 | -------------------------------------------------------------------------------- /test-otpc-conn.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file test-otpc-conn.hpp 3 | * @author Alex Ott 4 | * 5 | * @brief Declaration of sync connection class 6 | * 7 | * 8 | */ 9 | 10 | #ifndef _TEST_OTPC_CONN_H 11 | #define _TEST_OTPC_CONN_H 1 12 | 13 | #include "common.h" 14 | 15 | /** 16 | * Class for handling connection in sync mode 17 | * 18 | */ 19 | class connection { 20 | struct hide_me {}; // Instead of having to make friend boost::make_shared() 21 | public: 22 | typedef boost::shared_ptr pointer; 23 | 24 | connection(ba::io_service& io_service, hide_me); 25 | 26 | /** 27 | * Create new connection 28 | * 29 | * @param io_service io_service in which this connection will work 30 | * 31 | * @return pointer to newly allocated object 32 | */ 33 | static pointer create(ba::io_service& io_service) { 34 | return boost::make_shared(boost::ref(io_service), hide_me()); 35 | } 36 | 37 | /** 38 | * Return socket, associated with this connection. This socket used in accept operation 39 | * 40 | * 41 | * @return reference to socket 42 | */ 43 | ba::ip::tcp::socket& socket() { 44 | return socket_; 45 | } 46 | void run(); 47 | 48 | private: 49 | ba::io_service& io_service_; /**< reference to io_service, in which work this connection */ 50 | ba::ip::tcp::socket socket_; /**< socket, associated with browser */ 51 | ba::streambuf buf; /**< buffer for request data */ 52 | const static std::string message_; /**< data, that we'll return to browser */ 53 | }; 54 | 55 | 56 | #endif /* _TEST_OTPC_CONN_H */ 57 | 58 | -------------------------------------------------------------------------------- /test-otpc-tp.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file test-ocmt-tp.cpp 3 | * @author Alex Ott 4 | * 5 | * @brief Implementation of 'stupid' www server with implementation of thread per 6 | * connection strategy and usage of threads pool for keeping number of threads limited. 7 | * Working threads use sync mode for input/output, but server thread work in async mode. 8 | * 9 | * Thread pool implemented by threadpool library, that you could obtain from 10 | * http://threadpool.sf.net 11 | * 12 | */ 13 | 14 | #include "test-otpc-conn.hpp" 15 | #include 16 | 17 | 18 | /** 19 | * Server class 20 | * 21 | */ 22 | class server : private boost::noncopyable { 23 | public: 24 | server(ba::io_service& io_service_accept, ba::io_service& io_service_execute, 25 | unsigned int thread_num_acceptors, unsigned int thread_num_executors, unsigned int port = 10001, std::string interface_address = ""); 26 | ~server(); 27 | 28 | private: 29 | void handle_accept(connection::pointer old_connection, const boost::system::error_code& e); 30 | 31 | ba::io_service& io_service_acceptors_; /**< reference to io_service */ 32 | ba::io_service& io_service_executors_; /**< reference to io_service */ 33 | ba::io_service::work work_acceptors_; /**< object to inform the io_service_acceptors_ when it has work to do */ 34 | ba::io_service::work work_executors_; /**< object to inform the io_service_executors_ when it has work to do */ 35 | std::vector thr_grp_acceptors_; /**< thread pool object for acceptors */ 36 | std::vector thr_grp_executors_; /**< thread pool object for executors */ 37 | const ba::ip::tcp::endpoint endpoint_; /**< object, that points to the connection endpoint */ 38 | ba::ip::tcp::acceptor acceptor_; /**< object, that accepts new connections */ 39 | }; 40 | 41 | /** 42 | * Initialize all needed data 43 | * 44 | * @param io_service reference to io_service 45 | * @param thnum number of threads in thread pool 46 | * @param port port to listen on, by default - 10001 47 | */ 48 | server::server(ba::io_service& io_service_acceptors, ba::io_service& io_service_executors, 49 | unsigned int thread_num_acceptors, unsigned int thread_num_executors, unsigned int port, std::string interface_address) 50 | : io_service_acceptors_(io_service_acceptors), 51 | io_service_executors_(io_service_executors), 52 | work_acceptors_(io_service_acceptors_), 53 | work_executors_(io_service_executors_), 54 | endpoint_(interface_address.empty()? 55 | (ba::ip::tcp::endpoint(ba::ip::tcp::v4(), port)): // INADDR_ANY for v4 (in6addr_any if the fix to v6) 56 | ba::ip::tcp::endpoint(ba::ip::address().from_string(interface_address), port) ), // specified ip address 57 | acceptor_(io_service_acceptors_, endpoint_) // By default set option to reuse the address (i.e. SO_REUSEADDR) 58 | { 59 | std::cout << endpoint_.address().to_string() << ":" << endpoint_.port() << std::endl; 60 | 61 | // create threads in pool for executors 62 | for(size_t i = 0; i < thread_num_executors; ++i) 63 | thr_grp_executors_.emplace_back(boost::bind(&boost::asio::io_service::run, &io_service_executors_)); 64 | 65 | // create threads in pool and start acceptors 66 | for(size_t i = 0; i < thread_num_acceptors; ++i) { 67 | // create threads in pool 68 | if(i != 0) // one main thread already in pool from: int main() { ... io_service_acceptors.run(); ... } 69 | thr_grp_acceptors_.emplace_back(boost::bind(&boost::asio::io_service::run, &io_service_acceptors_)); 70 | 71 | // create next connection, that will accepted next 72 | connection::pointer new_connection = connection::create(io_service_executors_); 73 | 74 | // start another acceptor in async mode 75 | acceptor_.async_accept(new_connection->socket(), 76 | boost::bind(&server::handle_accept, this, new_connection, 77 | ba::placeholders::error)); 78 | } 79 | } 80 | 81 | /** 82 | * Wait for all executing threads 83 | * 84 | */ 85 | server::~server() { 86 | io_service_acceptors_.stop(); 87 | io_service_executors_.stop(); 88 | for(auto &i : thr_grp_acceptors_) i.join(); 89 | for(auto &i : thr_grp_executors_) i.join(); 90 | } 91 | 92 | /** 93 | * Run when new connection is accepted 94 | * 95 | * @param e reference to error object 96 | */ 97 | void server::handle_accept(connection::pointer old_connection, const boost::system::error_code& e) { 98 | if (!e) { 99 | // schedule new task to thread pool 100 | io_service_executors_.post(boost::bind(&connection::run, old_connection)); 101 | // create next connection, that will accepted 102 | connection::pointer new_connection = connection::create(io_service_executors_); 103 | auto &socket = new_connection->socket(); 104 | // start new accept operation 105 | acceptor_.async_accept(socket, 106 | boost::bind(&server::handle_accept, this, 107 | boost::move(new_connection), // doesn't copy and doesn't use the atomic counter with memory barrier 108 | ba::placeholders::error)); 109 | } 110 | } 111 | 112 | /** 113 | * Main routine 114 | * 115 | * @param argc number of arguments 116 | * @param argv pointers to arguments 117 | * 118 | * @return error code 119 | */ 120 | int main(int argc, char** argv) { 121 | try { 122 | int thread_num_acceptors = 2, thread_num_executors = 10, port = 10001; 123 | std::string interface_address; 124 | // read number of threads in thread pool from command line, if provided 125 | if(argc > 1) 126 | thread_num_acceptors = boost::lexical_cast(argv[1]); 127 | if(argc > 2) 128 | thread_num_executors = boost::lexical_cast(argv[2]); 129 | // read port number from command line, if provided 130 | if(argc > 3) 131 | port = boost::lexical_cast(argv[3]); 132 | // read local interface address from command line, if provided 133 | if(argc > 4) 134 | interface_address = argv[4]; 135 | ba::io_service io_service_acceptors, io_service_executors; 136 | // construct new server object 137 | server s(io_service_acceptors, io_service_executors, thread_num_acceptors, thread_num_executors, port, interface_address); 138 | // run io_service object, that perform all dispatch operations 139 | io_service_acceptors.run(); 140 | } catch (std::exception& e) { 141 | std::cerr << e.what() << std::endl; 142 | } 143 | 144 | return 0; 145 | } 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /test-otpc.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file test-ocmt.cpp 3 | * @author Alex Ott 4 | * 5 | * @brief Implementation of 'stupid' www server with implementation of thread per 6 | * connection strategy. Working threads use sync mode for input/output, but server thread 7 | * work in async mode. 8 | * 9 | * 10 | */ 11 | 12 | #include "test-otpc-conn.hpp" 13 | #include 14 | 15 | /** 16 | * Server class 17 | * 18 | */ 19 | class server : private boost::noncopyable { 20 | public: 21 | server(ba::io_service& io_service, int port=10001); 22 | 23 | private: 24 | void handle_accept(const boost::system::error_code& e); 25 | 26 | ba::io_service& io_service_; /**< reference to io_service */ 27 | ba::ip::tcp::acceptor acceptor_; /**< object, that accepts new connections */ 28 | connection::pointer new_connection_; /**< pointer to connection, that will proceed next */ 29 | }; 30 | 31 | /** 32 | * Initialize all needed data 33 | * 34 | * @param io_service reference to io_service 35 | * @param port port to listen on, by default - 10001 36 | */ 37 | server::server(ba::io_service& io_service,int port) 38 | : io_service_(io_service), 39 | acceptor_(io_service_, ba::ip::tcp::endpoint(ba::ip::tcp::v4(), port)), 40 | new_connection_(connection::create(io_service_)) { 41 | // start acceptor in async mode 42 | acceptor_.async_accept(new_connection_->socket(), 43 | boost::bind(&server::handle_accept, this, 44 | ba::placeholders::error)); 45 | } 46 | 47 | /** 48 | * Run when new connection is accepted 49 | * 50 | * @param e reference to error object 51 | */ 52 | void server::handle_accept(const boost::system::error_code& e) { 53 | if (!e) { 54 | // run connection in new thread 55 | boost::thread t(boost::bind(&connection::run, new_connection_)); 56 | // create next connection, that will accepted 57 | new_connection_=connection::create(io_service_); 58 | // start new accept operation 59 | acceptor_.async_accept(new_connection_->socket(), 60 | boost::bind(&server::handle_accept, this, 61 | ba::placeholders::error)); 62 | } 63 | } 64 | 65 | /** 66 | * Main routine 67 | * 68 | * @param argc number of arguments 69 | * @param argv pointers to arguments 70 | * 71 | * @return error code 72 | */ 73 | int main(int argc, char** argv) { 74 | try { 75 | int port=10001; 76 | // read port number from command line, if provided 77 | if(argc > 1) 78 | port=boost::lexical_cast(argv[1]); 79 | ba::io_service io_service; 80 | // construct new server object 81 | server s(io_service,port); 82 | // run io_service object, that perform all dispatch operations 83 | io_service.run(); 84 | } catch (std::exception& e) { 85 | std::cerr << e.what() << std::endl; 86 | } 87 | 88 | return 0; 89 | } 90 | 91 | 92 | --------------------------------------------------------------------------------