Created
February 13, 2017 16:39
-
-
Save ruipacheco/3a9361c8055f538c7bae09ed0d4a5b5d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment