1 | // |
2 | // detail/reactive_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_REACTIVE_SOCKET_SERVICE_HPP |
12 | #define BOOST_ASIO_DETAIL_REACTIVE_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 <boost/asio/buffer.hpp> |
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/buffer_sequence_adapter.hpp> |
28 | #include <boost/asio/detail/noncopyable.hpp> |
29 | #include <boost/asio/detail/reactive_null_buffers_op.hpp> |
30 | #include <boost/asio/detail/reactive_socket_accept_op.hpp> |
31 | #include <boost/asio/detail/reactive_socket_connect_op.hpp> |
32 | #include <boost/asio/detail/reactive_socket_recvfrom_op.hpp> |
33 | #include <boost/asio/detail/reactive_socket_sendto_op.hpp> |
34 | #include <boost/asio/detail/reactive_socket_service_base.hpp> |
35 | #include <boost/asio/detail/reactor.hpp> |
36 | #include <boost/asio/detail/reactor_op.hpp> |
37 | #include <boost/asio/detail/socket_holder.hpp> |
38 | #include <boost/asio/detail/socket_ops.hpp> |
39 | #include <boost/asio/detail/socket_types.hpp> |
40 | |
41 | #include <boost/asio/detail/push_options.hpp> |
42 | |
43 | namespace boost { |
44 | namespace asio { |
45 | namespace detail { |
46 | |
47 | template <typename Protocol> |
48 | class reactive_socket_service : |
49 | public reactive_socket_service_base |
50 | { |
51 | public: |
52 | // The protocol type. |
53 | typedef Protocol protocol_type; |
54 | |
55 | // The endpoint type. |
56 | typedef typename Protocol::endpoint endpoint_type; |
57 | |
58 | // The native type of a socket. |
59 | typedef socket_type native_handle_type; |
60 | |
61 | // The implementation type of the socket. |
62 | struct implementation_type : |
63 | reactive_socket_service_base::base_implementation_type |
64 | { |
65 | // Default constructor. |
66 | implementation_type() |
67 | : protocol_(endpoint_type().protocol()) |
68 | { |
69 | } |
70 | |
71 | // The protocol associated with the socket. |
72 | protocol_type protocol_; |
73 | }; |
74 | |
75 | // Constructor. |
76 | reactive_socket_service(boost::asio::io_service& io_service) |
77 | : reactive_socket_service_base(io_service) |
78 | { |
79 | } |
80 | |
81 | // Move-construct a new socket implementation. |
82 | void move_construct(implementation_type& impl, |
83 | implementation_type& other_impl) |
84 | { |
85 | this->base_move_construct(impl, other_impl); |
86 | |
87 | impl.protocol_ = other_impl.protocol_; |
88 | other_impl.protocol_ = endpoint_type().protocol(); |
89 | } |
90 | |
91 | // Move-assign from another socket implementation. |
92 | void move_assign(implementation_type& impl, |
93 | reactive_socket_service_base& other_service, |
94 | implementation_type& other_impl) |
95 | { |
96 | this->base_move_assign(impl, other_service, other_impl); |
97 | |
98 | impl.protocol_ = other_impl.protocol_; |
99 | other_impl.protocol_ = endpoint_type().protocol(); |
100 | } |
101 | |
102 | // Move-construct a new socket implementation from another protocol type. |
103 | template <typename Protocol1> |
104 | void converting_move_construct(implementation_type& impl, |
105 | typename reactive_socket_service< |
106 | Protocol1>::implementation_type& other_impl) |
107 | { |
108 | this->base_move_construct(impl, other_impl); |
109 | |
110 | impl.protocol_ = protocol_type(other_impl.protocol_); |
111 | other_impl.protocol_ = typename Protocol1::endpoint().protocol(); |
112 | } |
113 | |
114 | // Open a new socket implementation. |
115 | boost::system::error_code open(implementation_type& impl, |
116 | const protocol_type& protocol, boost::system::error_code& ec) |
117 | { |
118 | if (!do_open(impl, af: protocol.family(), |
119 | type: protocol.type(), protocol: protocol.protocol(), ec)) |
120 | impl.protocol_ = protocol; |
121 | return ec; |
122 | } |
123 | |
124 | // Assign a native socket to a socket implementation. |
125 | boost::system::error_code assign(implementation_type& impl, |
126 | const protocol_type& protocol, const native_handle_type& native_socket, |
127 | boost::system::error_code& ec) |
128 | { |
129 | if (!do_assign(impl, type: protocol.type(), native_socket, ec)) |
130 | impl.protocol_ = protocol; |
131 | return ec; |
132 | } |
133 | |
134 | // Get the native socket representation. |
135 | native_handle_type native_handle(implementation_type& impl) |
136 | { |
137 | return impl.socket_; |
138 | } |
139 | |
140 | // Bind the socket to the specified local endpoint. |
141 | boost::system::error_code bind(implementation_type& impl, |
142 | const endpoint_type& endpoint, boost::system::error_code& ec) |
143 | { |
144 | socket_ops::bind(s: impl.socket_, addr: endpoint.data(), addrlen: endpoint.size(), ec); |
145 | return ec; |
146 | } |
147 | |
148 | // Set a socket option. |
149 | template <typename Option> |
150 | boost::system::error_code set_option(implementation_type& impl, |
151 | const Option& option, boost::system::error_code& ec) |
152 | { |
153 | socket_ops::setsockopt(s: impl.socket_, state&: impl.state_, |
154 | level: option.level(impl.protocol_), optname: option.name(impl.protocol_), |
155 | optval: option.data(impl.protocol_), optlen: option.size(impl.protocol_), ec); |
156 | return ec; |
157 | } |
158 | |
159 | // Set a socket option. |
160 | template <typename Option> |
161 | boost::system::error_code get_option(const implementation_type& impl, |
162 | Option& option, boost::system::error_code& ec) const |
163 | { |
164 | std::size_t size = option.size(impl.protocol_); |
165 | socket_ops::getsockopt(s: impl.socket_, state: impl.state_, |
166 | level: option.level(impl.protocol_), optname: option.name(impl.protocol_), |
167 | optval: option.data(impl.protocol_), optlen: &size, ec); |
168 | if (!ec) |
169 | option.resize(impl.protocol_, size); |
170 | return ec; |
171 | } |
172 | |
173 | // Get the local endpoint. |
174 | endpoint_type local_endpoint(const implementation_type& impl, |
175 | boost::system::error_code& ec) const |
176 | { |
177 | endpoint_type endpoint; |
178 | std::size_t addr_len = endpoint.capacity(); |
179 | if (socket_ops::getsockname(s: impl.socket_, addr: endpoint.data(), addrlen: &addr_len, ec)) |
180 | return endpoint_type(); |
181 | endpoint.resize(addr_len); |
182 | return endpoint; |
183 | } |
184 | |
185 | // Get the remote endpoint. |
186 | endpoint_type remote_endpoint(const implementation_type& impl, |
187 | boost::system::error_code& ec) const |
188 | { |
189 | endpoint_type endpoint; |
190 | std::size_t addr_len = endpoint.capacity(); |
191 | if (socket_ops::getpeername(s: impl.socket_, |
192 | addr: endpoint.data(), addrlen: &addr_len, cached: false, ec)) |
193 | return endpoint_type(); |
194 | endpoint.resize(addr_len); |
195 | return endpoint; |
196 | } |
197 | |
198 | // Send a datagram to the specified endpoint. Returns the number of bytes |
199 | // sent. |
200 | template <typename ConstBufferSequence> |
201 | size_t send_to(implementation_type& impl, const ConstBufferSequence& buffers, |
202 | const endpoint_type& destination, socket_base::message_flags flags, |
203 | boost::system::error_code& ec) |
204 | { |
205 | buffer_sequence_adapter<boost::asio::const_buffer, |
206 | ConstBufferSequence> bufs(buffers); |
207 | |
208 | return socket_ops::sync_sendto(s: impl.socket_, state: impl.state_, |
209 | bufs: bufs.buffers(), count: bufs.count(), flags, |
210 | addr: destination.data(), addrlen: destination.size(), ec); |
211 | } |
212 | |
213 | // Wait until data can be sent without blocking. |
214 | size_t send_to(implementation_type& impl, const null_buffers&, |
215 | const endpoint_type&, socket_base::message_flags, |
216 | boost::system::error_code& ec) |
217 | { |
218 | // Wait for socket to become ready. |
219 | socket_ops::poll_write(s: impl.socket_, state: impl.state_, ec); |
220 | |
221 | return 0; |
222 | } |
223 | |
224 | // Start an asynchronous send. The data being sent must be valid for the |
225 | // lifetime of the asynchronous operation. |
226 | template <typename ConstBufferSequence, typename Handler> |
227 | void async_send_to(implementation_type& impl, |
228 | const ConstBufferSequence& buffers, |
229 | const endpoint_type& destination, socket_base::message_flags flags, |
230 | Handler& handler) |
231 | { |
232 | bool is_continuation = |
233 | boost_asio_handler_cont_helpers::is_continuation(handler); |
234 | |
235 | // Allocate and construct an operation to wrap the handler. |
236 | typedef reactive_socket_sendto_op<ConstBufferSequence, |
237 | endpoint_type, Handler> op; |
238 | typename op::ptr p = { boost::asio::detail::addressof(handler), |
239 | boost_asio_handler_alloc_helpers::allocate( |
240 | sizeof(op), handler), 0 }; |
241 | p.p = new (p.v) op(impl.socket_, buffers, destination, flags, handler); |
242 | |
243 | BOOST_ASIO_HANDLER_CREATION((p.p, "socket" , &impl, "async_send_to" )); |
244 | |
245 | start_op(impl, op_type: reactor::write_op, op: p.p, is_continuation, is_non_blocking: true, noop: false); |
246 | p.v = p.p = 0; |
247 | } |
248 | |
249 | // Start an asynchronous wait until data can be sent without blocking. |
250 | template <typename Handler> |
251 | void async_send_to(implementation_type& impl, const null_buffers&, |
252 | const endpoint_type&, socket_base::message_flags, Handler& handler) |
253 | { |
254 | bool is_continuation = |
255 | boost_asio_handler_cont_helpers::is_continuation(handler); |
256 | |
257 | // Allocate and construct an operation to wrap the handler. |
258 | typedef reactive_null_buffers_op<Handler> op; |
259 | typename op::ptr p = { boost::asio::detail::addressof(handler), |
260 | boost_asio_handler_alloc_helpers::allocate( |
261 | sizeof(op), handler), 0 }; |
262 | p.p = new (p.v) op(handler); |
263 | |
264 | BOOST_ASIO_HANDLER_CREATION((p.p, "socket" , |
265 | &impl, "async_send_to(null_buffers)" )); |
266 | |
267 | start_op(impl, op_type: reactor::write_op, op: p.p, is_continuation, is_non_blocking: false, noop: false); |
268 | p.v = p.p = 0; |
269 | } |
270 | |
271 | // Receive a datagram with the endpoint of the sender. Returns the number of |
272 | // bytes received. |
273 | template <typename MutableBufferSequence> |
274 | size_t receive_from(implementation_type& impl, |
275 | const MutableBufferSequence& buffers, |
276 | endpoint_type& sender_endpoint, socket_base::message_flags flags, |
277 | boost::system::error_code& ec) |
278 | { |
279 | buffer_sequence_adapter<boost::asio::mutable_buffer, |
280 | MutableBufferSequence> bufs(buffers); |
281 | |
282 | std::size_t addr_len = sender_endpoint.capacity(); |
283 | std::size_t bytes_recvd = socket_ops::sync_recvfrom( |
284 | s: impl.socket_, state: impl.state_, bufs: bufs.buffers(), count: bufs.count(), |
285 | flags, addr: sender_endpoint.data(), addrlen: &addr_len, ec); |
286 | |
287 | if (!ec) |
288 | sender_endpoint.resize(addr_len); |
289 | |
290 | return bytes_recvd; |
291 | } |
292 | |
293 | // Wait until data can be received without blocking. |
294 | size_t receive_from(implementation_type& impl, const null_buffers&, |
295 | endpoint_type& sender_endpoint, socket_base::message_flags, |
296 | boost::system::error_code& ec) |
297 | { |
298 | // Wait for socket to become ready. |
299 | socket_ops::poll_read(s: impl.socket_, state: impl.state_, ec); |
300 | |
301 | // Reset endpoint since it can be given no sensible value at this time. |
302 | sender_endpoint = endpoint_type(); |
303 | |
304 | return 0; |
305 | } |
306 | |
307 | // Start an asynchronous receive. The buffer for the data being received and |
308 | // the sender_endpoint object must both be valid for the lifetime of the |
309 | // asynchronous operation. |
310 | template <typename MutableBufferSequence, typename Handler> |
311 | void async_receive_from(implementation_type& impl, |
312 | const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, |
313 | socket_base::message_flags flags, Handler& handler) |
314 | { |
315 | bool is_continuation = |
316 | boost_asio_handler_cont_helpers::is_continuation(handler); |
317 | |
318 | // Allocate and construct an operation to wrap the handler. |
319 | typedef reactive_socket_recvfrom_op<MutableBufferSequence, |
320 | endpoint_type, Handler> op; |
321 | typename op::ptr p = { boost::asio::detail::addressof(handler), |
322 | boost_asio_handler_alloc_helpers::allocate( |
323 | sizeof(op), handler), 0 }; |
324 | int protocol = impl.protocol_.type(); |
325 | p.p = new (p.v) op(impl.socket_, protocol, |
326 | buffers, sender_endpoint, flags, handler); |
327 | |
328 | BOOST_ASIO_HANDLER_CREATION((p.p, "socket" , |
329 | &impl, "async_receive_from" )); |
330 | |
331 | start_op(impl, |
332 | op_type: (flags & socket_base::message_out_of_band) |
333 | ? reactor::except_op : reactor::read_op, |
334 | op: p.p, is_continuation, is_non_blocking: true, noop: false); |
335 | p.v = p.p = 0; |
336 | } |
337 | |
338 | // Wait until data can be received without blocking. |
339 | template <typename Handler> |
340 | void async_receive_from(implementation_type& impl, |
341 | const null_buffers&, endpoint_type& sender_endpoint, |
342 | socket_base::message_flags flags, Handler& handler) |
343 | { |
344 | bool is_continuation = |
345 | boost_asio_handler_cont_helpers::is_continuation(handler); |
346 | |
347 | // Allocate and construct an operation to wrap the handler. |
348 | typedef reactive_null_buffers_op<Handler> op; |
349 | typename op::ptr p = { boost::asio::detail::addressof(handler), |
350 | boost_asio_handler_alloc_helpers::allocate( |
351 | sizeof(op), handler), 0 }; |
352 | p.p = new (p.v) op(handler); |
353 | |
354 | BOOST_ASIO_HANDLER_CREATION((p.p, "socket" , |
355 | &impl, "async_receive_from(null_buffers)" )); |
356 | |
357 | // Reset endpoint since it can be given no sensible value at this time. |
358 | sender_endpoint = endpoint_type(); |
359 | |
360 | start_op(impl, |
361 | op_type: (flags & socket_base::message_out_of_band) |
362 | ? reactor::except_op : reactor::read_op, |
363 | op: p.p, is_continuation, is_non_blocking: false, noop: false); |
364 | p.v = p.p = 0; |
365 | } |
366 | |
367 | // Accept a new connection. |
368 | template <typename Socket> |
369 | boost::system::error_code accept(implementation_type& impl, |
370 | Socket& peer, endpoint_type* peer_endpoint, boost::system::error_code& ec) |
371 | { |
372 | // We cannot accept a socket that is already open. |
373 | if (peer.is_open()) |
374 | { |
375 | ec = boost::asio::error::already_open; |
376 | return ec; |
377 | } |
378 | |
379 | std::size_t addr_len = peer_endpoint ? peer_endpoint->capacity() : 0; |
380 | socket_holder new_socket(socket_ops::sync_accept(s: impl.socket_, |
381 | state: impl.state_, addr: peer_endpoint ? peer_endpoint->data() : 0, |
382 | addrlen: peer_endpoint ? &addr_len : 0, ec)); |
383 | |
384 | // On success, assign new connection to peer socket object. |
385 | if (new_socket.get() != invalid_socket) |
386 | { |
387 | if (peer_endpoint) |
388 | peer_endpoint->resize(addr_len); |
389 | if (!peer.assign(impl.protocol_, new_socket.get(), ec)) |
390 | new_socket.release(); |
391 | } |
392 | |
393 | return ec; |
394 | } |
395 | |
396 | // Start an asynchronous accept. The peer and peer_endpoint objects |
397 | // must be valid until the accept's handler is invoked. |
398 | template <typename Socket, typename Handler> |
399 | void async_accept(implementation_type& impl, Socket& peer, |
400 | endpoint_type* peer_endpoint, Handler& handler) |
401 | { |
402 | bool is_continuation = |
403 | boost_asio_handler_cont_helpers::is_continuation(handler); |
404 | |
405 | // Allocate and construct an operation to wrap the handler. |
406 | typedef reactive_socket_accept_op<Socket, Protocol, Handler> op; |
407 | typename op::ptr p = { boost::asio::detail::addressof(handler), |
408 | boost_asio_handler_alloc_helpers::allocate( |
409 | sizeof(op), handler), 0 }; |
410 | p.p = new (p.v) op(impl.socket_, impl.state_, peer, |
411 | impl.protocol_, peer_endpoint, handler); |
412 | |
413 | BOOST_ASIO_HANDLER_CREATION((p.p, "socket" , &impl, "async_accept" )); |
414 | |
415 | start_accept_op(impl, op: p.p, is_continuation, peer_is_open: peer.is_open()); |
416 | p.v = p.p = 0; |
417 | } |
418 | |
419 | // Connect the socket to the specified endpoint. |
420 | boost::system::error_code connect(implementation_type& impl, |
421 | const endpoint_type& peer_endpoint, boost::system::error_code& ec) |
422 | { |
423 | socket_ops::sync_connect(s: impl.socket_, |
424 | addr: peer_endpoint.data(), addrlen: peer_endpoint.size(), ec); |
425 | return ec; |
426 | } |
427 | |
428 | // Start an asynchronous connect. |
429 | template <typename Handler> |
430 | void async_connect(implementation_type& impl, |
431 | const endpoint_type& peer_endpoint, Handler& handler) |
432 | { |
433 | bool is_continuation = |
434 | boost_asio_handler_cont_helpers::is_continuation(handler); |
435 | |
436 | // Allocate and construct an operation to wrap the handler. |
437 | typedef reactive_socket_connect_op<Handler> op; |
438 | typename op::ptr p = { boost::asio::detail::addressof(handler), |
439 | boost_asio_handler_alloc_helpers::allocate( |
440 | sizeof(op), handler), 0 }; |
441 | p.p = new (p.v) op(impl.socket_, handler); |
442 | |
443 | BOOST_ASIO_HANDLER_CREATION((p.p, "socket" , &impl, "async_connect" )); |
444 | |
445 | start_connect_op(impl, op: p.p, is_continuation, |
446 | addr: peer_endpoint.data(), addrlen: peer_endpoint.size()); |
447 | p.v = p.p = 0; |
448 | } |
449 | }; |
450 | |
451 | } // namespace detail |
452 | } // namespace asio |
453 | } // namespace boost |
454 | |
455 | #include <boost/asio/detail/pop_options.hpp> |
456 | |
457 | #endif // !defined(BOOST_ASIO_HAS_IOCP) |
458 | |
459 | #endif // BOOST_ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_HPP |
460 | |