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