186 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			186 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			C++
		
	
	
	
| //
 | |
| // detail/impl/socket_select_interrupter.ipp
 | |
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
| //
 | |
| // Copyright (c) 2003-2023 Christopher M. Kohlhoff (chris at kohlhoff dot com)
 | |
| //
 | |
| // Distributed under the Boost Software License, Version 1.0. (See accompanying
 | |
| // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
 | |
| //
 | |
| 
 | |
| #ifndef ASIO_DETAIL_IMPL_SOCKET_SELECT_INTERRUPTER_IPP
 | |
| #define ASIO_DETAIL_IMPL_SOCKET_SELECT_INTERRUPTER_IPP
 | |
| 
 | |
| #if defined(_MSC_VER) && (_MSC_VER >= 1200)
 | |
| # pragma once
 | |
| #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
 | |
| 
 | |
| #include "asio/detail/config.hpp"
 | |
| 
 | |
| #if !defined(ASIO_WINDOWS_RUNTIME)
 | |
| 
 | |
| #if defined(ASIO_WINDOWS) \
 | |
|   || defined(__CYGWIN__) \
 | |
|   || defined(__SYMBIAN32__)
 | |
| 
 | |
| #include <cstdlib>
 | |
| #include "asio/detail/socket_holder.hpp"
 | |
| #include "asio/detail/socket_ops.hpp"
 | |
| #include "asio/detail/socket_select_interrupter.hpp"
 | |
| #include "asio/detail/throw_error.hpp"
 | |
| #include "asio/error.hpp"
 | |
| 
 | |
| #include "asio/detail/push_options.hpp"
 | |
| 
 | |
| namespace asio {
 | |
| namespace detail {
 | |
| 
 | |
| socket_select_interrupter::socket_select_interrupter()
 | |
| {
 | |
|   open_descriptors();
 | |
| }
 | |
| 
 | |
| void socket_select_interrupter::open_descriptors()
 | |
| {
 | |
|   asio::error_code ec;
 | |
|   socket_holder acceptor(socket_ops::socket(
 | |
|         AF_INET, SOCK_STREAM, IPPROTO_TCP, ec));
 | |
|   if (acceptor.get() == invalid_socket)
 | |
|     asio::detail::throw_error(ec, "socket_select_interrupter");
 | |
| 
 | |
|   int opt = 1;
 | |
|   socket_ops::state_type acceptor_state = 0;
 | |
|   socket_ops::setsockopt(acceptor.get(), acceptor_state,
 | |
|       SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt), ec);
 | |
| 
 | |
|   using namespace std; // For memset.
 | |
|   sockaddr_in4_type addr;
 | |
|   std::size_t addr_len = sizeof(addr);
 | |
|   memset(&addr, 0, sizeof(addr));
 | |
|   addr.sin_family = AF_INET;
 | |
|   addr.sin_addr.s_addr = socket_ops::host_to_network_long(INADDR_LOOPBACK);
 | |
|   addr.sin_port = 0;
 | |
|   if (socket_ops::bind(acceptor.get(), &addr,
 | |
|         addr_len, ec) == socket_error_retval)
 | |
|     asio::detail::throw_error(ec, "socket_select_interrupter");
 | |
| 
 | |
|   if (socket_ops::getsockname(acceptor.get(), &addr,
 | |
|         &addr_len, ec) == socket_error_retval)
 | |
|     asio::detail::throw_error(ec, "socket_select_interrupter");
 | |
| 
 | |
|   // Some broken firewalls on Windows will intermittently cause getsockname to
 | |
|   // return 0.0.0.0 when the socket is actually bound to 127.0.0.1. We
 | |
|   // explicitly specify the target address here to work around this problem.
 | |
|   if (addr.sin_addr.s_addr == socket_ops::host_to_network_long(INADDR_ANY))
 | |
|     addr.sin_addr.s_addr = socket_ops::host_to_network_long(INADDR_LOOPBACK);
 | |
| 
 | |
|   if (socket_ops::listen(acceptor.get(),
 | |
|         SOMAXCONN, ec) == socket_error_retval)
 | |
|     asio::detail::throw_error(ec, "socket_select_interrupter");
 | |
| 
 | |
|   socket_holder client(socket_ops::socket(
 | |
|         AF_INET, SOCK_STREAM, IPPROTO_TCP, ec));
 | |
|   if (client.get() == invalid_socket)
 | |
|     asio::detail::throw_error(ec, "socket_select_interrupter");
 | |
| 
 | |
|   if (socket_ops::connect(client.get(), &addr,
 | |
|         addr_len, ec) == socket_error_retval)
 | |
|     asio::detail::throw_error(ec, "socket_select_interrupter");
 | |
| 
 | |
|   socket_holder server(socket_ops::accept(acceptor.get(), 0, 0, ec));
 | |
|   if (server.get() == invalid_socket)
 | |
|     asio::detail::throw_error(ec, "socket_select_interrupter");
 | |
|   
 | |
|   ioctl_arg_type non_blocking = 1;
 | |
|   socket_ops::state_type client_state = 0;
 | |
|   if (socket_ops::ioctl(client.get(), client_state,
 | |
|         FIONBIO, &non_blocking, ec))
 | |
|     asio::detail::throw_error(ec, "socket_select_interrupter");
 | |
| 
 | |
|   opt = 1;
 | |
|   socket_ops::setsockopt(client.get(), client_state,
 | |
|       IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt), ec);
 | |
| 
 | |
|   non_blocking = 1;
 | |
|   socket_ops::state_type server_state = 0;
 | |
|   if (socket_ops::ioctl(server.get(), server_state,
 | |
|         FIONBIO, &non_blocking, ec))
 | |
|     asio::detail::throw_error(ec, "socket_select_interrupter");
 | |
| 
 | |
|   opt = 1;
 | |
|   socket_ops::setsockopt(server.get(), server_state,
 | |
|       IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt), ec);
 | |
| 
 | |
|   read_descriptor_ = server.release();
 | |
|   write_descriptor_ = client.release();
 | |
| }
 | |
| 
 | |
| socket_select_interrupter::~socket_select_interrupter()
 | |
| {
 | |
|   close_descriptors();
 | |
| }
 | |
| 
 | |
| void socket_select_interrupter::close_descriptors()
 | |
| {
 | |
|   asio::error_code ec;
 | |
|   socket_ops::state_type state = socket_ops::internal_non_blocking;
 | |
|   if (read_descriptor_ != invalid_socket)
 | |
|     socket_ops::close(read_descriptor_, state, true, ec);
 | |
|   if (write_descriptor_ != invalid_socket)
 | |
|     socket_ops::close(write_descriptor_, state, true, ec);
 | |
| }
 | |
| 
 | |
| void socket_select_interrupter::recreate()
 | |
| {
 | |
|   close_descriptors();
 | |
| 
 | |
|   write_descriptor_ = invalid_socket;
 | |
|   read_descriptor_ = invalid_socket;
 | |
| 
 | |
|   open_descriptors();
 | |
| }
 | |
| 
 | |
| void socket_select_interrupter::interrupt()
 | |
| {
 | |
|   char byte = 0;
 | |
|   socket_ops::buf b;
 | |
|   socket_ops::init_buf(b, &byte, 1);
 | |
|   asio::error_code ec;
 | |
|   socket_ops::send(write_descriptor_, &b, 1, 0, ec);
 | |
| }
 | |
| 
 | |
| bool socket_select_interrupter::reset()
 | |
| {
 | |
|   char data[1024];
 | |
|   socket_ops::buf b;
 | |
|   socket_ops::init_buf(b, data, sizeof(data));
 | |
|   asio::error_code ec;
 | |
|   for (;;)
 | |
|   {
 | |
|     int bytes_read = socket_ops::recv(read_descriptor_, &b, 1, 0, ec);
 | |
|     if (bytes_read == sizeof(data))
 | |
|       continue;
 | |
|     if (bytes_read > 0)
 | |
|       return true;
 | |
|     if (bytes_read == 0)
 | |
|       return false;
 | |
|     if (ec == asio::error::would_block
 | |
|         || ec == asio::error::try_again)
 | |
|       return true;
 | |
|     return false;
 | |
|   }
 | |
| }
 | |
| 
 | |
| } // namespace detail
 | |
| } // namespace asio
 | |
| 
 | |
| #include "asio/detail/pop_options.hpp"
 | |
| 
 | |
| #endif // defined(ASIO_WINDOWS)
 | |
|        // || defined(__CYGWIN__)
 | |
|        // || defined(__SYMBIAN32__)
 | |
| 
 | |
| #endif // !defined(ASIO_WINDOWS_RUNTIME)
 | |
| 
 | |
| #endif // ASIO_DETAIL_IMPL_SOCKET_SELECT_INTERRUPTER_IPP
 |