1 //
2 // detail/win_iocp_socket_service.hpp
3 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2015 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 #ifndef BOOST_ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_HPP
12 #define BOOST_ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_HPP
13 
14 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
15 # pragma once
16 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
17 
18 #include <boost/asio/detail/config.hpp>
19 
20 #if defined(BOOST_ASIO_HAS_IOCP)
21 
22 #include <cstring>
23 #include <boost/asio/error.hpp>
24 #include <boost/asio/io_service.hpp>
25 #include <boost/asio/socket_base.hpp>
26 #include <boost/asio/detail/addressof.hpp>
27 #include <boost/asio/detail/bind_handler.hpp>
28 #include <boost/asio/detail/buffer_sequence_adapter.hpp>
29 #include <boost/asio/detail/fenced_block.hpp>
30 #include <boost/asio/detail/handler_alloc_helpers.hpp>
31 #include <boost/asio/detail/handler_invoke_helpers.hpp>
32 #include <boost/asio/detail/mutex.hpp>
33 #include <boost/asio/detail/operation.hpp>
34 #include <boost/asio/detail/reactor.hpp>
35 #include <boost/asio/detail/reactor_op.hpp>
36 #include <boost/asio/detail/socket_holder.hpp>
37 #include <boost/asio/detail/socket_ops.hpp>
38 #include <boost/asio/detail/socket_types.hpp>
39 #include <boost/asio/detail/win_iocp_io_service.hpp>
40 #include <boost/asio/detail/win_iocp_null_buffers_op.hpp>
41 #include <boost/asio/detail/win_iocp_socket_accept_op.hpp>
42 #include <boost/asio/detail/win_iocp_socket_connect_op.hpp>
43 #include <boost/asio/detail/win_iocp_socket_recvfrom_op.hpp>
44 #include <boost/asio/detail/win_iocp_socket_send_op.hpp>
45 #include <boost/asio/detail/win_iocp_socket_service_base.hpp>
46 
47 #include <boost/asio/detail/push_options.hpp>
48 
49 namespace boost {
50 namespace asio {
51 namespace detail {
52 
53 template <typename Protocol>
54 class win_iocp_socket_service : public win_iocp_socket_service_base
55 {
56 public:
57   // The protocol type.
58   typedef Protocol protocol_type;
59 
60   // The endpoint type.
61   typedef typename Protocol::endpoint endpoint_type;
62 
63   // The native type of a socket.
64   class native_handle_type
65   {
66   public:
native_handle_type(socket_type s)67     native_handle_type(socket_type s)
68       : socket_(s),
69         have_remote_endpoint_(false)
70     {
71     }
72 
native_handle_type(socket_type s,const endpoint_type & ep)73     native_handle_type(socket_type s, const endpoint_type& ep)
74       : socket_(s),
75         have_remote_endpoint_(true),
76         remote_endpoint_(ep)
77     {
78     }
79 
operator =(socket_type s)80     void operator=(socket_type s)
81     {
82       socket_ = s;
83       have_remote_endpoint_ = false;
84       remote_endpoint_ = endpoint_type();
85     }
86 
operator socket_type() const87     operator socket_type() const
88     {
89       return socket_;
90     }
91 
have_remote_endpoint() const92     bool have_remote_endpoint() const
93     {
94       return have_remote_endpoint_;
95     }
96 
remote_endpoint() const97     endpoint_type remote_endpoint() const
98     {
99       return remote_endpoint_;
100     }
101 
102   private:
103     socket_type socket_;
104     bool have_remote_endpoint_;
105     endpoint_type remote_endpoint_;
106   };
107 
108   // The implementation type of the socket.
109   struct implementation_type :
110     win_iocp_socket_service_base::base_implementation_type
111   {
112     // Default constructor.
implementation_typeboost::asio::detail::win_iocp_socket_service::implementation_type113     implementation_type()
114       : protocol_(endpoint_type().protocol()),
115         have_remote_endpoint_(false),
116         remote_endpoint_()
117     {
118     }
119 
120     // The protocol associated with the socket.
121     protocol_type protocol_;
122 
123     // Whether we have a cached remote endpoint.
124     bool have_remote_endpoint_;
125 
126     // A cached remote endpoint.
127     endpoint_type remote_endpoint_;
128   };
129 
130   // Constructor.
win_iocp_socket_service(boost::asio::io_service & io_service)131   win_iocp_socket_service(boost::asio::io_service& io_service)
132     : win_iocp_socket_service_base(io_service)
133   {
134   }
135 
136   // Move-construct a new socket implementation.
move_construct(implementation_type & impl,implementation_type & other_impl)137   void move_construct(implementation_type& impl,
138       implementation_type& other_impl)
139   {
140     this->base_move_construct(impl, other_impl);
141 
142     impl.protocol_ = other_impl.protocol_;
143     other_impl.protocol_ = endpoint_type().protocol();
144 
145     impl.have_remote_endpoint_ = other_impl.have_remote_endpoint_;
146     other_impl.have_remote_endpoint_ = false;
147 
148     impl.remote_endpoint_ = other_impl.remote_endpoint_;
149     other_impl.remote_endpoint_ = endpoint_type();
150   }
151 
152   // Move-assign from another socket implementation.
move_assign(implementation_type & impl,win_iocp_socket_service_base & other_service,implementation_type & other_impl)153   void move_assign(implementation_type& impl,
154       win_iocp_socket_service_base& other_service,
155       implementation_type& other_impl)
156   {
157     this->base_move_assign(impl, other_service, other_impl);
158 
159     impl.protocol_ = other_impl.protocol_;
160     other_impl.protocol_ = endpoint_type().protocol();
161 
162     impl.have_remote_endpoint_ = other_impl.have_remote_endpoint_;
163     other_impl.have_remote_endpoint_ = false;
164 
165     impl.remote_endpoint_ = other_impl.remote_endpoint_;
166     other_impl.remote_endpoint_ = endpoint_type();
167   }
168 
169   // Move-construct a new socket implementation from another protocol type.
170   template <typename Protocol1>
converting_move_construct(implementation_type & impl,typename win_iocp_socket_service<Protocol1>::implementation_type & other_impl)171   void converting_move_construct(implementation_type& impl,
172       typename win_iocp_socket_service<
173         Protocol1>::implementation_type& other_impl)
174   {
175     this->base_move_construct(impl, other_impl);
176 
177     impl.protocol_ = protocol_type(other_impl.protocol_);
178     other_impl.protocol_ = typename Protocol1::endpoint().protocol();
179 
180     impl.have_remote_endpoint_ = other_impl.have_remote_endpoint_;
181     other_impl.have_remote_endpoint_ = false;
182 
183     impl.remote_endpoint_ = other_impl.remote_endpoint_;
184     other_impl.remote_endpoint_ = typename Protocol1::endpoint();
185   }
186 
187   // Open a new socket implementation.
open(implementation_type & impl,const protocol_type & protocol,boost::system::error_code & ec)188   boost::system::error_code open(implementation_type& impl,
189       const protocol_type& protocol, boost::system::error_code& ec)
190   {
191     if (!do_open(impl, protocol.family(),
192           protocol.type(), protocol.protocol(), ec))
193     {
194       impl.protocol_ = protocol;
195       impl.have_remote_endpoint_ = false;
196       impl.remote_endpoint_ = endpoint_type();
197     }
198     return ec;
199   }
200 
201   // Assign a native socket to a socket implementation.
assign(implementation_type & impl,const protocol_type & protocol,const native_handle_type & native_socket,boost::system::error_code & ec)202   boost::system::error_code assign(implementation_type& impl,
203       const protocol_type& protocol, const native_handle_type& native_socket,
204       boost::system::error_code& ec)
205   {
206     if (!do_assign(impl, protocol.type(), native_socket, ec))
207     {
208       impl.protocol_ = protocol;
209       impl.have_remote_endpoint_ = native_socket.have_remote_endpoint();
210       impl.remote_endpoint_ = native_socket.remote_endpoint();
211     }
212     return ec;
213   }
214 
215   // Get the native socket representation.
native_handle(implementation_type & impl)216   native_handle_type native_handle(implementation_type& impl)
217   {
218     if (impl.have_remote_endpoint_)
219       return native_handle_type(impl.socket_, impl.remote_endpoint_);
220     return native_handle_type(impl.socket_);
221   }
222 
223   // Bind the socket to the specified local endpoint.
bind(implementation_type & impl,const endpoint_type & endpoint,boost::system::error_code & ec)224   boost::system::error_code bind(implementation_type& impl,
225       const endpoint_type& endpoint, boost::system::error_code& ec)
226   {
227     socket_ops::bind(impl.socket_, endpoint.data(), endpoint.size(), ec);
228     return ec;
229   }
230 
231   // Set a socket option.
232   template <typename Option>
set_option(implementation_type & impl,const Option & option,boost::system::error_code & ec)233   boost::system::error_code set_option(implementation_type& impl,
234       const Option& option, boost::system::error_code& ec)
235   {
236     socket_ops::setsockopt(impl.socket_, impl.state_,
237         option.level(impl.protocol_), option.name(impl.protocol_),
238         option.data(impl.protocol_), option.size(impl.protocol_), ec);
239     return ec;
240   }
241 
242   // Set a socket option.
243   template <typename Option>
get_option(const implementation_type & impl,Option & option,boost::system::error_code & ec) const244   boost::system::error_code get_option(const implementation_type& impl,
245       Option& option, boost::system::error_code& ec) const
246   {
247     std::size_t size = option.size(impl.protocol_);
248     socket_ops::getsockopt(impl.socket_, impl.state_,
249         option.level(impl.protocol_), option.name(impl.protocol_),
250         option.data(impl.protocol_), &size, ec);
251     if (!ec)
252       option.resize(impl.protocol_, size);
253     return ec;
254   }
255 
256   // Get the local endpoint.
local_endpoint(const implementation_type & impl,boost::system::error_code & ec) const257   endpoint_type local_endpoint(const implementation_type& impl,
258       boost::system::error_code& ec) const
259   {
260     endpoint_type endpoint;
261     std::size_t addr_len = endpoint.capacity();
262     if (socket_ops::getsockname(impl.socket_, endpoint.data(), &addr_len, ec))
263       return endpoint_type();
264     endpoint.resize(addr_len);
265     return endpoint;
266   }
267 
268   // Get the remote endpoint.
remote_endpoint(const implementation_type & impl,boost::system::error_code & ec) const269   endpoint_type remote_endpoint(const implementation_type& impl,
270       boost::system::error_code& ec) const
271   {
272     endpoint_type endpoint = impl.remote_endpoint_;
273     std::size_t addr_len = endpoint.capacity();
274     if (socket_ops::getpeername(impl.socket_, endpoint.data(),
275           &addr_len, impl.have_remote_endpoint_, ec))
276       return endpoint_type();
277     endpoint.resize(addr_len);
278     return endpoint;
279   }
280 
281   // Send a datagram to the specified endpoint. Returns the number of bytes
282   // sent.
283   template <typename ConstBufferSequence>
send_to(implementation_type & impl,const ConstBufferSequence & buffers,const endpoint_type & destination,socket_base::message_flags flags,boost::system::error_code & ec)284   size_t send_to(implementation_type& impl, const ConstBufferSequence& buffers,
285       const endpoint_type& destination, socket_base::message_flags flags,
286       boost::system::error_code& ec)
287   {
288     buffer_sequence_adapter<boost::asio::const_buffer,
289         ConstBufferSequence> bufs(buffers);
290 
291     return socket_ops::sync_sendto(impl.socket_, impl.state_,
292         bufs.buffers(), bufs.count(), flags,
293         destination.data(), destination.size(), ec);
294   }
295 
296   // Wait until data can be sent without blocking.
send_to(implementation_type & impl,const null_buffers &,const endpoint_type &,socket_base::message_flags,boost::system::error_code & ec)297   size_t send_to(implementation_type& impl, const null_buffers&,
298       const endpoint_type&, socket_base::message_flags,
299       boost::system::error_code& ec)
300   {
301     // Wait for socket to become ready.
302     socket_ops::poll_write(impl.socket_, impl.state_, ec);
303 
304     return 0;
305   }
306 
307   // Start an asynchronous send. The data being sent must be valid for the
308   // lifetime of the asynchronous operation.
309   template <typename ConstBufferSequence, typename Handler>
async_send_to(implementation_type & impl,const ConstBufferSequence & buffers,const endpoint_type & destination,socket_base::message_flags flags,Handler & handler)310   void async_send_to(implementation_type& impl,
311       const ConstBufferSequence& buffers, const endpoint_type& destination,
312       socket_base::message_flags flags, Handler& handler)
313   {
314     // Allocate and construct an operation to wrap the handler.
315     typedef win_iocp_socket_send_op<ConstBufferSequence, Handler> op;
316     typename op::ptr p = { boost::asio::detail::addressof(handler),
317       boost_asio_handler_alloc_helpers::allocate(
318         sizeof(op), handler), 0 };
319     p.p = new (p.v) op(impl.cancel_token_, buffers, handler);
320 
321     BOOST_ASIO_HANDLER_CREATION((p.p, "socket", &impl, "async_send_to"));
322 
323     buffer_sequence_adapter<boost::asio::const_buffer,
324         ConstBufferSequence> bufs(buffers);
325 
326     start_send_to_op(impl, bufs.buffers(), bufs.count(),
327         destination.data(), static_cast<int>(destination.size()),
328         flags, p.p);
329     p.v = p.p = 0;
330   }
331 
332   // Start an asynchronous wait until data can be sent without blocking.
333   template <typename Handler>
async_send_to(implementation_type & impl,const null_buffers &,const endpoint_type &,socket_base::message_flags,Handler & handler)334   void async_send_to(implementation_type& impl, const null_buffers&,
335       const endpoint_type&, socket_base::message_flags, Handler& handler)
336   {
337     // Allocate and construct an operation to wrap the handler.
338     typedef win_iocp_null_buffers_op<Handler> op;
339     typename op::ptr p = { boost::asio::detail::addressof(handler),
340       boost_asio_handler_alloc_helpers::allocate(
341         sizeof(op), handler), 0 };
342     p.p = new (p.v) op(impl.cancel_token_, handler);
343 
344     BOOST_ASIO_HANDLER_CREATION((p.p, "socket",
345           &impl, "async_send_to(null_buffers)"));
346 
347     start_reactor_op(impl, reactor::write_op, p.p);
348     p.v = p.p = 0;
349   }
350 
351   // Receive a datagram with the endpoint of the sender. Returns the number of
352   // bytes received.
353   template <typename MutableBufferSequence>
receive_from(implementation_type & impl,const MutableBufferSequence & buffers,endpoint_type & sender_endpoint,socket_base::message_flags flags,boost::system::error_code & ec)354   size_t receive_from(implementation_type& impl,
355       const MutableBufferSequence& buffers,
356       endpoint_type& sender_endpoint, socket_base::message_flags flags,
357       boost::system::error_code& ec)
358   {
359     buffer_sequence_adapter<boost::asio::mutable_buffer,
360         MutableBufferSequence> bufs(buffers);
361 
362     std::size_t addr_len = sender_endpoint.capacity();
363     std::size_t bytes_recvd = socket_ops::sync_recvfrom(
364         impl.socket_, impl.state_, bufs.buffers(), bufs.count(),
365         flags, sender_endpoint.data(), &addr_len, ec);
366 
367     if (!ec)
368       sender_endpoint.resize(addr_len);
369 
370     return bytes_recvd;
371   }
372 
373   // Wait until data can be received without blocking.
receive_from(implementation_type & impl,const null_buffers &,endpoint_type & sender_endpoint,socket_base::message_flags,boost::system::error_code & ec)374   size_t receive_from(implementation_type& impl,
375       const null_buffers&, endpoint_type& sender_endpoint,
376       socket_base::message_flags, boost::system::error_code& ec)
377   {
378     // Wait for socket to become ready.
379     socket_ops::poll_read(impl.socket_, impl.state_, ec);
380 
381     // Reset endpoint since it can be given no sensible value at this time.
382     sender_endpoint = endpoint_type();
383 
384     return 0;
385   }
386 
387   // Start an asynchronous receive. The buffer for the data being received and
388   // the sender_endpoint object must both be valid for the lifetime of the
389   // asynchronous operation.
390   template <typename MutableBufferSequence, typename Handler>
async_receive_from(implementation_type & impl,const MutableBufferSequence & buffers,endpoint_type & sender_endp,socket_base::message_flags flags,Handler & handler)391   void async_receive_from(implementation_type& impl,
392       const MutableBufferSequence& buffers, endpoint_type& sender_endp,
393       socket_base::message_flags flags, Handler& handler)
394   {
395     // Allocate and construct an operation to wrap the handler.
396     typedef win_iocp_socket_recvfrom_op<
397       MutableBufferSequence, endpoint_type, Handler> op;
398     typename op::ptr p = { boost::asio::detail::addressof(handler),
399       boost_asio_handler_alloc_helpers::allocate(
400         sizeof(op), handler), 0 };
401     p.p = new (p.v) op(sender_endp, impl.cancel_token_, buffers, handler);
402 
403     BOOST_ASIO_HANDLER_CREATION((p.p, "socket", &impl, "async_receive_from"));
404 
405     buffer_sequence_adapter<boost::asio::mutable_buffer,
406         MutableBufferSequence> bufs(buffers);
407 
408     start_receive_from_op(impl, bufs.buffers(), bufs.count(),
409         sender_endp.data(), flags, &p.p->endpoint_size(), p.p);
410     p.v = p.p = 0;
411   }
412 
413   // Wait until data can be received without blocking.
414   template <typename Handler>
async_receive_from(implementation_type & impl,const null_buffers &,endpoint_type & sender_endpoint,socket_base::message_flags flags,Handler & handler)415   void async_receive_from(implementation_type& impl,
416       const null_buffers&, endpoint_type& sender_endpoint,
417       socket_base::message_flags flags, Handler& handler)
418   {
419     // Allocate and construct an operation to wrap the handler.
420     typedef win_iocp_null_buffers_op<Handler> op;
421     typename op::ptr p = { boost::asio::detail::addressof(handler),
422       boost_asio_handler_alloc_helpers::allocate(
423         sizeof(op), handler), 0 };
424     p.p = new (p.v) op(impl.cancel_token_, handler);
425 
426     BOOST_ASIO_HANDLER_CREATION((p.p, "socket", &impl,
427           "async_receive_from(null_buffers)"));
428 
429     // Reset endpoint since it can be given no sensible value at this time.
430     sender_endpoint = endpoint_type();
431 
432     start_null_buffers_receive_op(impl, flags, p.p);
433     p.v = p.p = 0;
434   }
435 
436   // Accept a new connection.
437   template <typename Socket>
accept(implementation_type & impl,Socket & peer,endpoint_type * peer_endpoint,boost::system::error_code & ec)438   boost::system::error_code accept(implementation_type& impl, Socket& peer,
439       endpoint_type* peer_endpoint, boost::system::error_code& ec)
440   {
441     // We cannot accept a socket that is already open.
442     if (peer.is_open())
443     {
444       ec = boost::asio::error::already_open;
445       return ec;
446     }
447 
448     std::size_t addr_len = peer_endpoint ? peer_endpoint->capacity() : 0;
449     socket_holder new_socket(socket_ops::sync_accept(impl.socket_,
450           impl.state_, peer_endpoint ? peer_endpoint->data() : 0,
451           peer_endpoint ? &addr_len : 0, ec));
452 
453     // On success, assign new connection to peer socket object.
454     if (new_socket.get() != invalid_socket)
455     {
456       if (peer_endpoint)
457         peer_endpoint->resize(addr_len);
458       if (!peer.assign(impl.protocol_, new_socket.get(), ec))
459         new_socket.release();
460     }
461 
462     return ec;
463   }
464 
465   // Start an asynchronous accept. The peer and peer_endpoint objects
466   // must be valid until the accept's handler is invoked.
467   template <typename Socket, typename Handler>
async_accept(implementation_type & impl,Socket & peer,endpoint_type * peer_endpoint,Handler & handler)468   void async_accept(implementation_type& impl, Socket& peer,
469       endpoint_type* peer_endpoint, Handler& handler)
470   {
471     // Allocate and construct an operation to wrap the handler.
472     typedef win_iocp_socket_accept_op<Socket, protocol_type, Handler> op;
473     typename op::ptr p = { boost::asio::detail::addressof(handler),
474       boost_asio_handler_alloc_helpers::allocate(
475         sizeof(op), handler), 0 };
476     bool enable_connection_aborted =
477       (impl.state_ & socket_ops::enable_connection_aborted) != 0;
478     p.p = new (p.v) op(*this, impl.socket_, peer, impl.protocol_,
479         peer_endpoint, enable_connection_aborted, handler);
480 
481     BOOST_ASIO_HANDLER_CREATION((p.p, "socket", &impl, "async_accept"));
482 
483     start_accept_op(impl, peer.is_open(), p.p->new_socket(),
484         impl.protocol_.family(), impl.protocol_.type(),
485         impl.protocol_.protocol(), p.p->output_buffer(),
486         p.p->address_length(), p.p);
487     p.v = p.p = 0;
488   }
489 
490   // Connect the socket to the specified endpoint.
connect(implementation_type & impl,const endpoint_type & peer_endpoint,boost::system::error_code & ec)491   boost::system::error_code connect(implementation_type& impl,
492       const endpoint_type& peer_endpoint, boost::system::error_code& ec)
493   {
494     socket_ops::sync_connect(impl.socket_,
495         peer_endpoint.data(), peer_endpoint.size(), ec);
496     return ec;
497   }
498 
499   // Start an asynchronous connect.
500   template <typename Handler>
async_connect(implementation_type & impl,const endpoint_type & peer_endpoint,Handler & handler)501   void async_connect(implementation_type& impl,
502       const endpoint_type& peer_endpoint, Handler& handler)
503   {
504     // Allocate and construct an operation to wrap the handler.
505     typedef win_iocp_socket_connect_op<Handler> op;
506     typename op::ptr p = { boost::asio::detail::addressof(handler),
507       boost_asio_handler_alloc_helpers::allocate(
508         sizeof(op), handler), 0 };
509     p.p = new (p.v) op(impl.socket_, handler);
510 
511     BOOST_ASIO_HANDLER_CREATION((p.p, "socket", &impl, "async_connect"));
512 
513     start_connect_op(impl, impl.protocol_.family(), impl.protocol_.type(),
514         peer_endpoint.data(), static_cast<int>(peer_endpoint.size()), p.p);
515     p.v = p.p = 0;
516   }
517 };
518 
519 } // namespace detail
520 } // namespace asio
521 } // namespace boost
522 
523 #include <boost/asio/detail/pop_options.hpp>
524 
525 #endif // defined(BOOST_ASIO_HAS_IOCP)
526 
527 #endif // BOOST_ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_HPP
528