| // |
| // third_party_lib.cpp |
| // ~~~~~~~~~~~~~~~~~~~ |
| // |
| // Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) |
| // |
| // Distributed under the Boost Software License, Version 1.0. (See accompanying |
| // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
| // |
| |
| #include <boost/asio.hpp> |
| #include <boost/array.hpp> |
| #include <boost/bind.hpp> |
| #include <boost/shared_ptr.hpp> |
| #include <boost/enable_shared_from_this.hpp> |
| #include <iostream> |
| |
| using boost::asio::ip::tcp; |
| |
| namespace third_party_lib { |
| |
| // Simulation of a third party library that wants to perform read and write |
| // operations directly on a socket. It needs to be polled to determine whether |
| // it requires a read or write operation, and notified when the socket is ready |
| // for reading or writing. |
| class session |
| { |
| public: |
| session(tcp::socket& socket) |
| : socket_(socket), |
| state_(reading) |
| { |
| } |
| |
| // Returns true if the third party library wants to be notified when the |
| // socket is ready for reading. |
| bool want_read() const |
| { |
| return state_ == reading; |
| } |
| |
| // Notify that third party library that it should perform its read operation. |
| void do_read(boost::system::error_code& ec) |
| { |
| if (std::size_t len = socket_.read_some(boost::asio::buffer(data_), ec)) |
| { |
| write_buffer_ = boost::asio::buffer(data_, len); |
| state_ = writing; |
| } |
| } |
| |
| // Returns true if the third party library wants to be notified when the |
| // socket is ready for writing. |
| bool want_write() const |
| { |
| return state_ == writing; |
| } |
| |
| // Notify that third party library that it should perform its write operation. |
| void do_write(boost::system::error_code& ec) |
| { |
| if (std::size_t len = socket_.write_some( |
| boost::asio::buffer(write_buffer_), ec)) |
| { |
| write_buffer_ = write_buffer_ + len; |
| state_ = boost::asio::buffer_size(write_buffer_) > 0 ? writing : reading; |
| } |
| } |
| |
| private: |
| tcp::socket& socket_; |
| enum { reading, writing } state_; |
| boost::array<char, 128> data_; |
| boost::asio::const_buffer write_buffer_; |
| }; |
| |
| } // namespace third_party_lib |
| |
| // The glue between asio's sockets and the third party library. |
| class connection |
| : public boost::enable_shared_from_this<connection> |
| { |
| public: |
| typedef boost::shared_ptr<connection> pointer; |
| |
| static pointer create(boost::asio::io_service& io_service) |
| { |
| return pointer(new connection(io_service)); |
| } |
| |
| tcp::socket& socket() |
| { |
| return socket_; |
| } |
| |
| void start() |
| { |
| // Put the socket into non-blocking mode. |
| tcp::socket::non_blocking_io non_blocking_io(true); |
| socket_.io_control(non_blocking_io); |
| |
| start_operations(); |
| } |
| |
| private: |
| connection(boost::asio::io_service& io_service) |
| : socket_(io_service), |
| session_impl_(socket_), |
| read_in_progress_(false), |
| write_in_progress_(false) |
| { |
| } |
| |
| void start_operations() |
| { |
| // Start a read operation if the third party library wants one. |
| if (session_impl_.want_read() && !read_in_progress_) |
| { |
| read_in_progress_ = true; |
| socket_.async_read_some( |
| boost::asio::null_buffers(), |
| boost::bind(&connection::handle_read, |
| shared_from_this(), |
| boost::asio::placeholders::error)); |
| } |
| |
| // Start a write operation if the third party library wants one. |
| if (session_impl_.want_write() && !write_in_progress_) |
| { |
| write_in_progress_ = true; |
| socket_.async_write_some( |
| boost::asio::null_buffers(), |
| boost::bind(&connection::handle_write, |
| shared_from_this(), |
| boost::asio::placeholders::error)); |
| } |
| } |
| |
| void handle_read(boost::system::error_code ec) |
| { |
| read_in_progress_ = false; |
| |
| // Notify third party library that it can perform a read. |
| if (!ec) |
| session_impl_.do_read(ec); |
| |
| // The third party library successfully performed a read on the socket. |
| // Start new read or write operations based on what it now wants. |
| if (!ec || ec == boost::asio::error::would_block) |
| start_operations(); |
| |
| // Otherwise, an error occurred. Closing the socket cancels any outstanding |
| // asynchronous read or write operations. The connection object will be |
| // destroyed automatically once those outstanding operations complete. |
| else |
| socket_.close(); |
| } |
| |
| void handle_write(boost::system::error_code ec) |
| { |
| write_in_progress_ = false; |
| |
| // Notify third party library that it can perform a write. |
| if (!ec) |
| session_impl_.do_write(ec); |
| |
| // The third party library successfully performed a write on the socket. |
| // Start new read or write operations based on what it now wants. |
| if (!ec || ec == boost::asio::error::would_block) |
| start_operations(); |
| |
| // Otherwise, an error occurred. Closing the socket cancels any outstanding |
| // asynchronous read or write operations. The connection object will be |
| // destroyed automatically once those outstanding operations complete. |
| else |
| socket_.close(); |
| } |
| |
| private: |
| tcp::socket socket_; |
| third_party_lib::session session_impl_; |
| bool read_in_progress_; |
| bool write_in_progress_; |
| }; |
| |
| class server |
| { |
| public: |
| server(boost::asio::io_service& io_service, unsigned short port) |
| : acceptor_(io_service, tcp::endpoint(tcp::v4(), port)) |
| { |
| start_accept(); |
| } |
| |
| private: |
| void start_accept() |
| { |
| connection::pointer new_connection = |
| connection::create(acceptor_.io_service()); |
| |
| acceptor_.async_accept(new_connection->socket(), |
| boost::bind(&server::handle_accept, this, new_connection, |
| boost::asio::placeholders::error)); |
| } |
| |
| void handle_accept(connection::pointer new_connection, |
| const boost::system::error_code& error) |
| { |
| if (!error) |
| { |
| new_connection->start(); |
| start_accept(); |
| } |
| } |
| |
| tcp::acceptor acceptor_; |
| }; |
| |
| int main(int argc, char* argv[]) |
| { |
| try |
| { |
| if (argc != 2) |
| { |
| std::cerr << "Usage: third_party_lib <port>\n"; |
| return 1; |
| } |
| |
| boost::asio::io_service io_service; |
| |
| using namespace std; // For atoi. |
| server s(io_service, atoi(argv[1])); |
| |
| io_service.run(); |
| } |
| catch (std::exception& e) |
| { |
| std::cerr << "Exception: " << e.what() << "\n"; |
| } |
| |
| return 0; |
| } |