mirror of
https://github.com/boostorg/asio.git
synced 2026-02-26 02:42:08 +00:00
Refactor reactive socket implementation so that synchronous read, write,
accept and connect operations don't modify data associated with the socket. [SVN r48491]
This commit is contained in:
@@ -74,10 +74,21 @@ public:
|
||||
|
||||
enum
|
||||
{
|
||||
user_set_non_blocking = 1, // The user wants a non-blocking socket.
|
||||
internal_non_blocking = 2, // The socket has been set non-blocking.
|
||||
enable_connection_aborted = 4, // User wants connection_aborted errors.
|
||||
user_set_linger = 8 // The user set the linger option.
|
||||
// The user wants a non-blocking socket.
|
||||
user_set_non_blocking = 1,
|
||||
|
||||
// The implementation wants a non-blocking socket (in order to be able to
|
||||
// perform asynchronous read and write operations).
|
||||
internal_non_blocking = 2,
|
||||
|
||||
// Helper "flag" used to determine whether the socket is non-blocking.
|
||||
non_blocking = user_set_non_blocking | internal_non_blocking,
|
||||
|
||||
// User wants connection_aborted errors, which are disabled by default.
|
||||
enable_connection_aborted = 4,
|
||||
|
||||
// The user set the linger option. Needs to be checked when closing.
|
||||
user_set_linger = 8
|
||||
};
|
||||
|
||||
// Flags indicating the current state of the socket.
|
||||
@@ -120,12 +131,12 @@ public:
|
||||
{
|
||||
reactor_.close_descriptor(impl.socket_, impl.reactor_data_);
|
||||
|
||||
if (impl.flags_ & implementation_type::internal_non_blocking)
|
||||
if (impl.flags_ & implementation_type::non_blocking)
|
||||
{
|
||||
ioctl_arg_type non_blocking = 0;
|
||||
boost::system::error_code ignored_ec;
|
||||
socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ignored_ec);
|
||||
impl.flags_ &= ~implementation_type::internal_non_blocking;
|
||||
impl.flags_ &= ~implementation_type::non_blocking;
|
||||
}
|
||||
|
||||
if (impl.flags_ & implementation_type::user_set_linger)
|
||||
@@ -214,12 +225,12 @@ public:
|
||||
{
|
||||
reactor_.close_descriptor(impl.socket_, impl.reactor_data_);
|
||||
|
||||
if (impl.flags_ & implementation_type::internal_non_blocking)
|
||||
if (impl.flags_ & implementation_type::non_blocking)
|
||||
{
|
||||
ioctl_arg_type non_blocking = 0;
|
||||
boost::system::error_code ignored_ec;
|
||||
socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ignored_ec);
|
||||
impl.flags_ &= ~implementation_type::internal_non_blocking;
|
||||
impl.flags_ &= ~implementation_type::non_blocking;
|
||||
}
|
||||
|
||||
if (socket_ops::close(impl.socket_, ec) == socket_error_retval)
|
||||
@@ -433,11 +444,35 @@ public:
|
||||
|
||||
if (command.name() == static_cast<int>(FIONBIO))
|
||||
{
|
||||
// Flags are manipulated in a temporary variable so that the socket
|
||||
// implementation is not updated unless the ioctl operation succeeds.
|
||||
unsigned char new_flags = impl.flags_;
|
||||
if (command.get())
|
||||
impl.flags_ |= implementation_type::user_set_non_blocking;
|
||||
new_flags |= implementation_type::user_set_non_blocking;
|
||||
else
|
||||
impl.flags_ &= ~implementation_type::user_set_non_blocking;
|
||||
ec = boost::system::error_code();
|
||||
new_flags &= ~implementation_type::user_set_non_blocking;
|
||||
|
||||
// Perform ioctl on socket if the non-blocking state has changed.
|
||||
if (!(impl.flags_ & implementation_type::non_blocking)
|
||||
&& (new_flags & implementation_type::non_blocking))
|
||||
{
|
||||
ioctl_arg_type non_blocking = 1;
|
||||
socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec);
|
||||
}
|
||||
else if ((impl.flags_ & implementation_type::non_blocking)
|
||||
&& !(new_flags & implementation_type::non_blocking))
|
||||
{
|
||||
ioctl_arg_type non_blocking = 0;
|
||||
socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec);
|
||||
}
|
||||
else
|
||||
{
|
||||
ec = boost::system::error_code();
|
||||
}
|
||||
|
||||
// Update socket implementation's flags only if successful.
|
||||
if (!ec)
|
||||
impl.flags_ = new_flags;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -530,18 +565,6 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Make socket non-blocking if user wants non-blocking.
|
||||
if (impl.flags_ & implementation_type::user_set_non_blocking)
|
||||
{
|
||||
if (!(impl.flags_ & implementation_type::internal_non_blocking))
|
||||
{
|
||||
ioctl_arg_type non_blocking = 1;
|
||||
if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
|
||||
return 0;
|
||||
impl.flags_ |= implementation_type::internal_non_blocking;
|
||||
}
|
||||
}
|
||||
|
||||
// Send the data.
|
||||
for (;;)
|
||||
{
|
||||
@@ -684,12 +707,15 @@ public:
|
||||
// Make socket non-blocking.
|
||||
if (!(impl.flags_ & implementation_type::internal_non_blocking))
|
||||
{
|
||||
ioctl_arg_type non_blocking = 1;
|
||||
boost::system::error_code ec;
|
||||
if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
|
||||
if (!(impl.flags_ & implementation_type::non_blocking))
|
||||
{
|
||||
this->get_io_service().post(bind_handler(handler, ec, 0));
|
||||
return;
|
||||
ioctl_arg_type non_blocking = 1;
|
||||
boost::system::error_code ec;
|
||||
if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
|
||||
{
|
||||
this->get_io_service().post(bind_handler(handler, ec, 0));
|
||||
return;
|
||||
}
|
||||
}
|
||||
impl.flags_ |= implementation_type::internal_non_blocking;
|
||||
}
|
||||
@@ -773,18 +799,6 @@ public:
|
||||
boost::asio::buffer_size(buffer));
|
||||
}
|
||||
|
||||
// Make socket non-blocking if user wants non-blocking.
|
||||
if (impl.flags_ & implementation_type::user_set_non_blocking)
|
||||
{
|
||||
if (!(impl.flags_ & implementation_type::internal_non_blocking))
|
||||
{
|
||||
ioctl_arg_type non_blocking = 1;
|
||||
if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
|
||||
return 0;
|
||||
impl.flags_ |= implementation_type::internal_non_blocking;
|
||||
}
|
||||
}
|
||||
|
||||
// Send the data.
|
||||
for (;;)
|
||||
{
|
||||
@@ -912,12 +926,15 @@ public:
|
||||
// Make socket non-blocking.
|
||||
if (!(impl.flags_ & implementation_type::internal_non_blocking))
|
||||
{
|
||||
ioctl_arg_type non_blocking = 1;
|
||||
boost::system::error_code ec;
|
||||
if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
|
||||
if (!(impl.flags_ & implementation_type::non_blocking))
|
||||
{
|
||||
this->get_io_service().post(bind_handler(handler, ec, 0));
|
||||
return;
|
||||
ioctl_arg_type non_blocking = 1;
|
||||
boost::system::error_code ec;
|
||||
if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
|
||||
{
|
||||
this->get_io_service().post(bind_handler(handler, ec, 0));
|
||||
return;
|
||||
}
|
||||
}
|
||||
impl.flags_ |= implementation_type::internal_non_blocking;
|
||||
}
|
||||
@@ -981,18 +998,6 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Make socket non-blocking if user wants non-blocking.
|
||||
if (impl.flags_ & implementation_type::user_set_non_blocking)
|
||||
{
|
||||
if (!(impl.flags_ & implementation_type::internal_non_blocking))
|
||||
{
|
||||
ioctl_arg_type non_blocking = 1;
|
||||
if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
|
||||
return 0;
|
||||
impl.flags_ |= implementation_type::internal_non_blocking;
|
||||
}
|
||||
}
|
||||
|
||||
// Receive some data.
|
||||
for (;;)
|
||||
{
|
||||
@@ -1148,12 +1153,15 @@ public:
|
||||
// Make socket non-blocking.
|
||||
if (!(impl.flags_ & implementation_type::internal_non_blocking))
|
||||
{
|
||||
ioctl_arg_type non_blocking = 1;
|
||||
boost::system::error_code ec;
|
||||
if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
|
||||
if (!(impl.flags_ & implementation_type::non_blocking))
|
||||
{
|
||||
this->get_io_service().post(bind_handler(handler, ec, 0));
|
||||
return;
|
||||
ioctl_arg_type non_blocking = 1;
|
||||
boost::system::error_code ec;
|
||||
if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
|
||||
{
|
||||
this->get_io_service().post(bind_handler(handler, ec, 0));
|
||||
return;
|
||||
}
|
||||
}
|
||||
impl.flags_ |= implementation_type::internal_non_blocking;
|
||||
}
|
||||
@@ -1225,18 +1233,6 @@ public:
|
||||
boost::asio::buffer_size(buffer));
|
||||
}
|
||||
|
||||
// Make socket non-blocking if user wants non-blocking.
|
||||
if (impl.flags_ & implementation_type::user_set_non_blocking)
|
||||
{
|
||||
if (!(impl.flags_ & implementation_type::internal_non_blocking))
|
||||
{
|
||||
ioctl_arg_type non_blocking = 1;
|
||||
if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
|
||||
return 0;
|
||||
impl.flags_ |= implementation_type::internal_non_blocking;
|
||||
}
|
||||
}
|
||||
|
||||
// Receive some data.
|
||||
for (;;)
|
||||
{
|
||||
@@ -1385,12 +1381,15 @@ public:
|
||||
// Make socket non-blocking.
|
||||
if (!(impl.flags_ & implementation_type::internal_non_blocking))
|
||||
{
|
||||
ioctl_arg_type non_blocking = 1;
|
||||
boost::system::error_code ec;
|
||||
if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
|
||||
if (!(impl.flags_ & implementation_type::non_blocking))
|
||||
{
|
||||
this->get_io_service().post(bind_handler(handler, ec, 0));
|
||||
return;
|
||||
ioctl_arg_type non_blocking = 1;
|
||||
boost::system::error_code ec;
|
||||
if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
|
||||
{
|
||||
this->get_io_service().post(bind_handler(handler, ec, 0));
|
||||
return;
|
||||
}
|
||||
}
|
||||
impl.flags_ |= implementation_type::internal_non_blocking;
|
||||
}
|
||||
@@ -1450,18 +1449,6 @@ public:
|
||||
return ec;
|
||||
}
|
||||
|
||||
// Make socket non-blocking if user wants non-blocking.
|
||||
if (impl.flags_ & implementation_type::user_set_non_blocking)
|
||||
{
|
||||
if (!(impl.flags_ & implementation_type::internal_non_blocking))
|
||||
{
|
||||
ioctl_arg_type non_blocking = 1;
|
||||
if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
|
||||
return ec;
|
||||
impl.flags_ |= implementation_type::internal_non_blocking;
|
||||
}
|
||||
}
|
||||
|
||||
// Accept a socket.
|
||||
for (;;)
|
||||
{
|
||||
@@ -1623,12 +1610,15 @@ public:
|
||||
// Make socket non-blocking.
|
||||
if (!(impl.flags_ & implementation_type::internal_non_blocking))
|
||||
{
|
||||
ioctl_arg_type non_blocking = 1;
|
||||
boost::system::error_code ec;
|
||||
if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
|
||||
if (!(impl.flags_ & implementation_type::non_blocking))
|
||||
{
|
||||
this->get_io_service().post(bind_handler(handler, ec));
|
||||
return;
|
||||
ioctl_arg_type non_blocking = 1;
|
||||
boost::system::error_code ec;
|
||||
if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
|
||||
{
|
||||
this->get_io_service().post(bind_handler(handler, ec));
|
||||
return;
|
||||
}
|
||||
}
|
||||
impl.flags_ |= implementation_type::internal_non_blocking;
|
||||
}
|
||||
@@ -1652,18 +1642,30 @@ public:
|
||||
return ec;
|
||||
}
|
||||
|
||||
if (impl.flags_ & implementation_type::internal_non_blocking)
|
||||
{
|
||||
// Mark the socket as blocking while we perform the connect.
|
||||
ioctl_arg_type non_blocking = 0;
|
||||
if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
|
||||
return ec;
|
||||
impl.flags_ &= ~implementation_type::internal_non_blocking;
|
||||
}
|
||||
|
||||
// Perform the connect operation.
|
||||
socket_ops::connect(impl.socket_,
|
||||
peer_endpoint.data(), peer_endpoint.size(), ec);
|
||||
if (ec != boost::asio::error::in_progress
|
||||
&& ec != boost::asio::error::would_block)
|
||||
{
|
||||
// The connect operation finished immediately.
|
||||
return ec;
|
||||
}
|
||||
|
||||
// Wait for socket to become ready.
|
||||
if (socket_ops::poll_connect(impl.socket_, ec) < 0)
|
||||
return ec;
|
||||
|
||||
// Get the error code from the connect operation.
|
||||
int connect_error = 0;
|
||||
size_t connect_error_len = sizeof(connect_error);
|
||||
if (socket_ops::getsockopt(impl.socket_, SOL_SOCKET, SO_ERROR,
|
||||
&connect_error, &connect_error_len, ec) == socket_error_retval)
|
||||
return ec;
|
||||
|
||||
// Return the result of the connect operation.
|
||||
ec = boost::system::error_code(connect_error,
|
||||
boost::asio::error::get_system_category());
|
||||
return ec;
|
||||
}
|
||||
|
||||
@@ -1731,12 +1733,15 @@ public:
|
||||
// Make socket non-blocking.
|
||||
if (!(impl.flags_ & implementation_type::internal_non_blocking))
|
||||
{
|
||||
ioctl_arg_type non_blocking = 1;
|
||||
boost::system::error_code ec;
|
||||
if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
|
||||
if (!(impl.flags_ & implementation_type::non_blocking))
|
||||
{
|
||||
this->get_io_service().post(bind_handler(handler, ec));
|
||||
return;
|
||||
ioctl_arg_type non_blocking = 1;
|
||||
boost::system::error_code ec;
|
||||
if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
|
||||
{
|
||||
this->get_io_service().post(bind_handler(handler, ec));
|
||||
return;
|
||||
}
|
||||
}
|
||||
impl.flags_ |= implementation_type::internal_non_blocking;
|
||||
}
|
||||
|
||||
@@ -702,6 +702,32 @@ inline int poll_write(socket_type s, boost::system::error_code& ec)
|
||||
#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
|
||||
}
|
||||
|
||||
inline int poll_connect(socket_type s, boost::system::error_code& ec)
|
||||
{
|
||||
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
|
||||
FD_SET write_fds;
|
||||
FD_ZERO(&write_fds);
|
||||
FD_SET(s, &write_fds);
|
||||
FD_SET except_fds;
|
||||
FD_ZERO(&except_fds);
|
||||
FD_SET(s, &except_fds);
|
||||
clear_error(ec);
|
||||
int result = error_wrapper(::select(s, 0, &write_fds, &except_fds, 0), ec);
|
||||
# if defined(UNDER_CE)
|
||||
if (result >= 0)
|
||||
clear_error(ec);
|
||||
# endif
|
||||
return result;
|
||||
#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
|
||||
pollfd fds;
|
||||
fds.fd = s;
|
||||
fds.events = POLLOUT;
|
||||
fds.revents = 0;
|
||||
clear_error(ec);
|
||||
return error_wrapper(::poll(&fds, 1, -1), ec);
|
||||
#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
|
||||
}
|
||||
|
||||
inline const char* inet_ntop(int af, const void* src, char* dest, size_t length,
|
||||
unsigned long scope_id, boost::system::error_code& ec)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user