transmit_file.cpp 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. //
  2. // transmit_file.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 <ctime>
  11. #include <iostream>
  12. #include <string>
  13. #include <boost/bind.hpp>
  14. #include <boost/shared_ptr.hpp>
  15. #include <boost/enable_shared_from_this.hpp>
  16. #include <boost/asio.hpp>
  17. #if defined(BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR)
  18. using boost::asio::ip::tcp;
  19. using boost::asio::windows::overlapped_ptr;
  20. using boost::asio::windows::random_access_handle;
  21. typedef boost::asio::basic_stream_socket<tcp,
  22. boost::asio::io_context::executor_type> tcp_socket;
  23. typedef boost::asio::basic_socket_acceptor<tcp,
  24. boost::asio::io_context::executor_type> tcp_acceptor;
  25. // A wrapper for the TransmitFile overlapped I/O operation.
  26. template <typename Handler>
  27. void transmit_file(tcp_socket& socket,
  28. random_access_handle& file, Handler handler)
  29. {
  30. // Construct an OVERLAPPED-derived object to contain the handler.
  31. overlapped_ptr overlapped(socket.get_executor().context(), handler);
  32. // Initiate the TransmitFile operation.
  33. BOOL ok = ::TransmitFile(socket.native_handle(),
  34. file.native_handle(), 0, 0, overlapped.get(), 0, 0);
  35. DWORD last_error = ::GetLastError();
  36. // Check if the operation completed immediately.
  37. if (!ok && last_error != ERROR_IO_PENDING)
  38. {
  39. // The operation completed immediately, so a completion notification needs
  40. // to be posted. When complete() is called, ownership of the OVERLAPPED-
  41. // derived object passes to the io_context.
  42. boost::system::error_code ec(last_error,
  43. boost::asio::error::get_system_category());
  44. overlapped.complete(ec, 0);
  45. }
  46. else
  47. {
  48. // The operation was successfully initiated, so ownership of the
  49. // OVERLAPPED-derived object has passed to the io_context.
  50. overlapped.release();
  51. }
  52. }
  53. class connection
  54. : public boost::enable_shared_from_this<connection>
  55. {
  56. public:
  57. typedef boost::shared_ptr<connection> pointer;
  58. static pointer create(boost::asio::io_context& io_context,
  59. const std::string& filename)
  60. {
  61. return pointer(new connection(io_context, filename));
  62. }
  63. tcp_socket& socket()
  64. {
  65. return socket_;
  66. }
  67. void start()
  68. {
  69. boost::system::error_code ec;
  70. file_.assign(::CreateFile(filename_.c_str(), GENERIC_READ, 0, 0,
  71. OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 0), ec);
  72. if (file_.is_open())
  73. {
  74. transmit_file(socket_, file_,
  75. boost::bind(&connection::handle_write, shared_from_this(),
  76. boost::asio::placeholders::error,
  77. boost::asio::placeholders::bytes_transferred));
  78. }
  79. }
  80. private:
  81. connection(boost::asio::io_context& io_context, const std::string& filename)
  82. : socket_(io_context),
  83. filename_(filename),
  84. file_(io_context)
  85. {
  86. }
  87. void handle_write(const boost::system::error_code& /*error*/,
  88. size_t /*bytes_transferred*/)
  89. {
  90. boost::system::error_code ignored_ec;
  91. socket_.shutdown(tcp_socket::shutdown_both, ignored_ec);
  92. }
  93. tcp_socket socket_;
  94. std::string filename_;
  95. random_access_handle file_;
  96. };
  97. class server
  98. {
  99. public:
  100. server(boost::asio::io_context& io_context,
  101. unsigned short port, const std::string& filename)
  102. : acceptor_(io_context, tcp::endpoint(tcp::v4(), port)),
  103. filename_(filename)
  104. {
  105. start_accept();
  106. }
  107. private:
  108. void start_accept()
  109. {
  110. connection::pointer new_connection =
  111. connection::create(acceptor_.get_executor().context(), filename_);
  112. acceptor_.async_accept(new_connection->socket(),
  113. boost::bind(&server::handle_accept, this, new_connection,
  114. boost::asio::placeholders::error));
  115. }
  116. void handle_accept(connection::pointer new_connection,
  117. const boost::system::error_code& error)
  118. {
  119. if (!error)
  120. {
  121. new_connection->start();
  122. }
  123. start_accept();
  124. }
  125. tcp_acceptor acceptor_;
  126. std::string filename_;
  127. };
  128. int main(int argc, char* argv[])
  129. {
  130. try
  131. {
  132. if (argc != 3)
  133. {
  134. std::cerr << "Usage: transmit_file <port> <filename>\n";
  135. return 1;
  136. }
  137. boost::asio::io_context io_context;
  138. using namespace std; // For atoi.
  139. server s(io_context, atoi(argv[1]), argv[2]);
  140. io_context.run();
  141. }
  142. catch (std::exception& e)
  143. {
  144. std::cerr << e.what() << std::endl;
  145. }
  146. return 0;
  147. }
  148. #else // defined(BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR)
  149. # error Overlapped I/O not available on this platform
  150. #endif // defined(BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR)