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