third_party_lib.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. //
  2. // third_party_lib.cpp
  3. // ~~~~~~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
  6. //
  7. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  8. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. //
  10. #include <boost/asio.hpp>
  11. #include <array>
  12. #include <iostream>
  13. #include <memory>
  14. using boost::asio::ip::tcp;
  15. namespace third_party_lib {
  16. // Simulation of a third party library that wants to perform read and write
  17. // operations directly on a socket. It needs to be polled to determine whether
  18. // it requires a read or write operation, and notified when the socket is ready
  19. // for reading or writing.
  20. class session
  21. {
  22. public:
  23. session(tcp::socket& socket)
  24. : socket_(socket)
  25. {
  26. }
  27. // Returns true if the third party library wants to be notified when the
  28. // socket is ready for reading.
  29. bool want_read() const
  30. {
  31. return state_ == reading;
  32. }
  33. // Notify that third party library that it should perform its read operation.
  34. void do_read(boost::system::error_code& ec)
  35. {
  36. if (std::size_t len = socket_.read_some(boost::asio::buffer(data_), ec))
  37. {
  38. write_buffer_ = boost::asio::buffer(data_, len);
  39. state_ = writing;
  40. }
  41. }
  42. // Returns true if the third party library wants to be notified when the
  43. // socket is ready for writing.
  44. bool want_write() const
  45. {
  46. return state_ == writing;
  47. }
  48. // Notify that third party library that it should perform its write operation.
  49. void do_write(boost::system::error_code& ec)
  50. {
  51. if (std::size_t len = socket_.write_some(
  52. boost::asio::buffer(write_buffer_), ec))
  53. {
  54. write_buffer_ = write_buffer_ + len;
  55. state_ = boost::asio::buffer_size(write_buffer_) > 0 ? writing : reading;
  56. }
  57. }
  58. private:
  59. tcp::socket& socket_;
  60. enum { reading, writing } state_ = reading;
  61. std::array<char, 128> data_;
  62. boost::asio::const_buffer write_buffer_;
  63. };
  64. } // namespace third_party_lib
  65. // The glue between asio's sockets and the third party library.
  66. class connection
  67. : public std::enable_shared_from_this<connection>
  68. {
  69. public:
  70. connection(tcp::socket socket)
  71. : socket_(std::move(socket))
  72. {
  73. }
  74. void start()
  75. {
  76. // Put the socket into non-blocking mode.
  77. socket_.non_blocking(true);
  78. do_operations();
  79. }
  80. private:
  81. void do_operations()
  82. {
  83. auto self(shared_from_this());
  84. // Start a read operation if the third party library wants one.
  85. if (session_impl_.want_read() && !read_in_progress_)
  86. {
  87. read_in_progress_ = true;
  88. socket_.async_wait(tcp::socket::wait_read,
  89. [this, self](boost::system::error_code ec)
  90. {
  91. read_in_progress_ = false;
  92. // Notify third party library that it can perform a read.
  93. if (!ec)
  94. session_impl_.do_read(ec);
  95. // The third party library successfully performed a read on the
  96. // socket. Start new read or write operations based on what it now
  97. // wants.
  98. if (!ec || ec == boost::asio::error::would_block)
  99. do_operations();
  100. // Otherwise, an error occurred. Closing the socket cancels any
  101. // outstanding asynchronous read or write operations. The
  102. // connection object will be destroyed automatically once those
  103. // outstanding operations complete.
  104. else
  105. socket_.close();
  106. });
  107. }
  108. // Start a write operation if the third party library wants one.
  109. if (session_impl_.want_write() && !write_in_progress_)
  110. {
  111. write_in_progress_ = true;
  112. socket_.async_wait(tcp::socket::wait_write,
  113. [this, self](boost::system::error_code ec)
  114. {
  115. write_in_progress_ = false;
  116. // Notify third party library that it can perform a write.
  117. if (!ec)
  118. session_impl_.do_write(ec);
  119. // The third party library successfully performed a write on the
  120. // socket. Start new read or write operations based on what it now
  121. // wants.
  122. if (!ec || ec == boost::asio::error::would_block)
  123. do_operations();
  124. // Otherwise, an error occurred. Closing the socket cancels any
  125. // outstanding asynchronous read or write operations. The
  126. // connection object will be destroyed automatically once those
  127. // outstanding operations complete.
  128. else
  129. socket_.close();
  130. });
  131. }
  132. }
  133. private:
  134. tcp::socket socket_;
  135. third_party_lib::session session_impl_{socket_};
  136. bool read_in_progress_ = false;
  137. bool write_in_progress_ = false;
  138. };
  139. class server
  140. {
  141. public:
  142. server(boost::asio::io_context& io_context, unsigned short port)
  143. : acceptor_(io_context, {tcp::v4(), port})
  144. {
  145. do_accept();
  146. }
  147. private:
  148. void do_accept()
  149. {
  150. acceptor_.async_accept(
  151. [this](boost::system::error_code ec, tcp::socket socket)
  152. {
  153. if (!ec)
  154. {
  155. std::make_shared<connection>(std::move(socket))->start();
  156. }
  157. do_accept();
  158. });
  159. }
  160. tcp::acceptor acceptor_;
  161. };
  162. int main(int argc, char* argv[])
  163. {
  164. try
  165. {
  166. if (argc != 2)
  167. {
  168. std::cerr << "Usage: third_party_lib <port>\n";
  169. return 1;
  170. }
  171. boost::asio::io_context io_context;
  172. server s(io_context, std::atoi(argv[1]));
  173. io_context.run();
  174. }
  175. catch (std::exception& e)
  176. {
  177. std::cerr << "Exception: " << e.what() << "\n";
  178. }
  179. return 0;
  180. }