2
0
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:
Christopher Kohlhoff
2008-08-31 09:01:59 +00:00
parent 2f86d9c815
commit 7176a41d0d
2 changed files with 141 additions and 110 deletions

View File

@@ -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;
}

View File

@@ -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)
{