socket_select_interrupter.ipp 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. //
  2. // detail/impl/socket_select_interrupter.ipp
  3. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2003-2018 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. #ifndef BOOST_ASIO_DETAIL_IMPL_SOCKET_SELECT_INTERRUPTER_IPP
  11. #define BOOST_ASIO_DETAIL_IMPL_SOCKET_SELECT_INTERRUPTER_IPP
  12. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  13. # pragma once
  14. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  15. #include <boost/asio/detail/config.hpp>
  16. #if !defined(BOOST_ASIO_WINDOWS_RUNTIME)
  17. #if defined(BOOST_ASIO_WINDOWS) \
  18. || defined(__CYGWIN__) \
  19. || defined(__SYMBIAN32__)
  20. #include <cstdlib>
  21. #include <boost/asio/detail/socket_holder.hpp>
  22. #include <boost/asio/detail/socket_ops.hpp>
  23. #include <boost/asio/detail/socket_select_interrupter.hpp>
  24. #include <boost/asio/detail/throw_error.hpp>
  25. #include <boost/asio/error.hpp>
  26. #include <boost/asio/detail/push_options.hpp>
  27. namespace boost {
  28. namespace asio {
  29. namespace detail {
  30. socket_select_interrupter::socket_select_interrupter()
  31. {
  32. open_descriptors();
  33. }
  34. void socket_select_interrupter::open_descriptors()
  35. {
  36. boost::system::error_code ec;
  37. socket_holder acceptor(socket_ops::socket(
  38. AF_INET, SOCK_STREAM, IPPROTO_TCP, ec));
  39. if (acceptor.get() == invalid_socket)
  40. boost::asio::detail::throw_error(ec, "socket_select_interrupter");
  41. int opt = 1;
  42. socket_ops::state_type acceptor_state = 0;
  43. socket_ops::setsockopt(acceptor.get(), acceptor_state,
  44. SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt), ec);
  45. using namespace std; // For memset.
  46. sockaddr_in4_type addr;
  47. std::size_t addr_len = sizeof(addr);
  48. memset(&addr, 0, sizeof(addr));
  49. addr.sin_family = AF_INET;
  50. addr.sin_addr.s_addr = socket_ops::host_to_network_long(INADDR_LOOPBACK);
  51. addr.sin_port = 0;
  52. if (socket_ops::bind(acceptor.get(), (const socket_addr_type*)&addr,
  53. addr_len, ec) == socket_error_retval)
  54. boost::asio::detail::throw_error(ec, "socket_select_interrupter");
  55. if (socket_ops::getsockname(acceptor.get(), (socket_addr_type*)&addr,
  56. &addr_len, ec) == socket_error_retval)
  57. boost::asio::detail::throw_error(ec, "socket_select_interrupter");
  58. // Some broken firewalls on Windows will intermittently cause getsockname to
  59. // return 0.0.0.0 when the socket is actually bound to 127.0.0.1. We
  60. // explicitly specify the target address here to work around this problem.
  61. if (addr.sin_addr.s_addr == socket_ops::host_to_network_long(INADDR_ANY))
  62. addr.sin_addr.s_addr = socket_ops::host_to_network_long(INADDR_LOOPBACK);
  63. if (socket_ops::listen(acceptor.get(),
  64. SOMAXCONN, ec) == socket_error_retval)
  65. boost::asio::detail::throw_error(ec, "socket_select_interrupter");
  66. socket_holder client(socket_ops::socket(
  67. AF_INET, SOCK_STREAM, IPPROTO_TCP, ec));
  68. if (client.get() == invalid_socket)
  69. boost::asio::detail::throw_error(ec, "socket_select_interrupter");
  70. if (socket_ops::connect(client.get(), (const socket_addr_type*)&addr,
  71. addr_len, ec) == socket_error_retval)
  72. boost::asio::detail::throw_error(ec, "socket_select_interrupter");
  73. socket_holder server(socket_ops::accept(acceptor.get(), 0, 0, ec));
  74. if (server.get() == invalid_socket)
  75. boost::asio::detail::throw_error(ec, "socket_select_interrupter");
  76. ioctl_arg_type non_blocking = 1;
  77. socket_ops::state_type client_state = 0;
  78. if (socket_ops::ioctl(client.get(), client_state,
  79. FIONBIO, &non_blocking, ec))
  80. boost::asio::detail::throw_error(ec, "socket_select_interrupter");
  81. opt = 1;
  82. socket_ops::setsockopt(client.get(), client_state,
  83. IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt), ec);
  84. non_blocking = 1;
  85. socket_ops::state_type server_state = 0;
  86. if (socket_ops::ioctl(server.get(), server_state,
  87. FIONBIO, &non_blocking, ec))
  88. boost::asio::detail::throw_error(ec, "socket_select_interrupter");
  89. opt = 1;
  90. socket_ops::setsockopt(server.get(), server_state,
  91. IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt), ec);
  92. read_descriptor_ = server.release();
  93. write_descriptor_ = client.release();
  94. }
  95. socket_select_interrupter::~socket_select_interrupter()
  96. {
  97. close_descriptors();
  98. }
  99. void socket_select_interrupter::close_descriptors()
  100. {
  101. boost::system::error_code ec;
  102. socket_ops::state_type state = socket_ops::internal_non_blocking;
  103. if (read_descriptor_ != invalid_socket)
  104. socket_ops::close(read_descriptor_, state, true, ec);
  105. if (write_descriptor_ != invalid_socket)
  106. socket_ops::close(write_descriptor_, state, true, ec);
  107. }
  108. void socket_select_interrupter::recreate()
  109. {
  110. close_descriptors();
  111. write_descriptor_ = invalid_socket;
  112. read_descriptor_ = invalid_socket;
  113. open_descriptors();
  114. }
  115. void socket_select_interrupter::interrupt()
  116. {
  117. char byte = 0;
  118. socket_ops::buf b;
  119. socket_ops::init_buf(b, &byte, 1);
  120. boost::system::error_code ec;
  121. socket_ops::send(write_descriptor_, &b, 1, 0, ec);
  122. }
  123. bool socket_select_interrupter::reset()
  124. {
  125. char data[1024];
  126. socket_ops::buf b;
  127. socket_ops::init_buf(b, data, sizeof(data));
  128. boost::system::error_code ec;
  129. int bytes_read = socket_ops::recv(read_descriptor_, &b, 1, 0, ec);
  130. bool was_interrupted = (bytes_read > 0);
  131. while (bytes_read == sizeof(data))
  132. bytes_read = socket_ops::recv(read_descriptor_, &b, 1, 0, ec);
  133. return was_interrupted;
  134. }
  135. } // namespace detail
  136. } // namespace asio
  137. } // namespace boost
  138. #include <boost/asio/detail/pop_options.hpp>
  139. #endif // defined(BOOST_ASIO_WINDOWS)
  140. // || defined(__CYGWIN__)
  141. // || defined(__SYMBIAN32__)
  142. #endif // !defined(BOOST_ASIO_WINDOWS_RUNTIME)
  143. #endif // BOOST_ASIO_DETAIL_IMPL_SOCKET_SELECT_INTERRUPTER_IPP