Add per-operation error handling policies.

This commit is contained in:
chris 2003-11-11 07:07:42 +00:00
parent c4761ad928
commit cc58f99de7
2 changed files with 206 additions and 59 deletions

View File

@ -21,6 +21,7 @@
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
#include "asio/detail/pop_options.hpp" #include "asio/detail/pop_options.hpp"
#include "asio/error_handler.hpp"
#include "asio/null_completion_context.hpp" #include "asio/null_completion_context.hpp"
#include "asio/service_factory.hpp" #include "asio/service_factory.hpp"
@ -68,22 +69,24 @@ public:
* @param addr An address on the local machine on which the acceptor will * @param addr An address on the local machine on which the acceptor will
* listen for new connections. * listen for new connections.
* *
* @param listen_queue The maximum length of the queue of pending
* connections. A value of 0 means use the default queue length.
*
* @throws socket_error Thrown on failure. * @throws socket_error Thrown on failure.
*/ */
template <typename Address> template <typename Address>
basic_socket_acceptor(demuxer_type& d, const Address& addr) basic_socket_acceptor(demuxer_type& d, const Address& addr,
int listen_queue = 0)
: service_(d.get_service(service_factory<Service>())), : service_(d.get_service(service_factory<Service>())),
impl_(service_type::null()) impl_(service_type::null())
{ {
service_.create(impl_, addr); service_.create(impl_, addr, listen_queue, default_error_handler());
} }
/// Construct an acceptor opened on the given address, specifying the /// Construct an acceptor opened on the given address.
/// maximum length of the queue of pending connetions.
/** /**
* This constructor creates an acceptor and automatically opens it to listen * This constructor creates an acceptor and automatically opens it to listen
* for new connections on the specified address. The maximum length of the * for new connections on the specified address.
* queue of pending connections is also supplied.
* *
* @param d The demuxer object that the acceptor will use to deliver * @param d The demuxer object that the acceptor will use to deliver
* completions for any asynchronous operations performed on the acceptor. * completions for any asynchronous operations performed on the acceptor.
@ -92,16 +95,22 @@ public:
* listen for new connections. * listen for new connections.
* *
* @param listen_queue The maximum length of the queue of pending * @param listen_queue The maximum length of the queue of pending
* connections. * connections. A value of 0 means use the default queue length.
* *
* @throws socket_error Thrown on failure. * @param error_handler The handler to be called when an error occurs or when
* the function completes successfully. Copies will be made of the handler as
* required. The equivalent function signature of the handler must be:
* @code void error_handler(
* const asio::socket_error& error // Result of operation
* ); @endcode
*/ */
template <typename Address> template <typename Address, typename Error_Handler>
basic_socket_acceptor(demuxer_type& d, const Address& addr, int listen_queue) basic_socket_acceptor(demuxer_type& d, const Address& addr, int listen_queue,
Error_Handler error_handler)
: service_(d.get_service(service_factory<Service>())), : service_(d.get_service(service_factory<Service>())),
impl_(service_type::null()) impl_(service_type::null())
{ {
service_.create(impl_, addr, listen_queue); service_.create(impl_, addr, listen_queue, error_handler);
} }
/// Destructor. /// Destructor.
@ -132,32 +141,39 @@ public:
* @param addr An address on the local machine on which the acceptor will * @param addr An address on the local machine on which the acceptor will
* listen for new connections. * listen for new connections.
* *
* @param listen_queue The maximum length of the queue of pending
* connections. A value of 0 means use the default queue length.
*
* @throws socket_error Thrown on failure. * @throws socket_error Thrown on failure.
*/ */
template <typename Address> template <typename Address>
void open(const Address& addr) void open(const Address& addr, int listen_queue = 0)
{ {
service_.create(impl_, addr); service_.create(impl_, addr, listen_queue, default_error_handler());
} }
/// Open the acceptor using the given address and length of the listen queue. /// Open the acceptor using the given address.
/** /**
* This function opens the acceptor to listen for new connections on the * This function opens the acceptor to listen for new connections on the
* specified address. The maximum length of the queue of pending connections * specified address.
* is also supplied.
* *
* @param addr An address on the local machine on which the acceptor will * @param addr An address on the local machine on which the acceptor will
* listen for new connections. * listen for new connections.
* *
* @param listen_queue The maximum length of the queue of pending * @param listen_queue The maximum length of the queue of pending
* connections. * connections. A value of 0 means use the default queue length.
* *
* @throws socket_error Thrown on failure. * @param error_handler The handler to be called when an error occurs or when
* the function completes successfully. Copies will be made of the handler as
* required. The equivalent function signature of the handler must be:
* @code void error_handler(
* const asio::socket_error& error // Result of operation
* ); @endcode
*/ */
template <typename Address> template <typename Address, typename Error_Handler>
void open(const Address& addr, int listen_queue) void open(const Address& addr, int listen_queue, Error_Handler error_handler)
{ {
service_.create(impl_, addr, listen_queue); service_.create(impl_, addr, listen_queue, error_handler);
} }
/// Close the acceptor. /// Close the acceptor.
@ -186,30 +202,70 @@ public:
/// Set an option on the acceptor. /// Set an option on the acceptor.
/** /**
* This function is used to set an option on the socket. * This function is used to set an option on the acceptor.
* *
* @param option The new option value to be set on the socket. * @param option The new option value to be set on the acceptor.
* *
* @throws socket_error Thrown on failure. * @throws socket_error Thrown on failure.
*/ */
template <typename Option> template <typename Option>
void set_option(const Option& option) void set_option(const Option& option)
{ {
service_.set_option(impl_, option); service_.set_option(impl_, option, default_error_handler());
}
/// Set an option on the acceptor.
/**
* This function is used to set an option on the acceptor.
*
* @param option The new option value to be set on the acceptor.
*
* @param error_handler The handler to be called when an error occurs or when
* the function completes successfully. Copies will be made of the handler as
* required. The equivalent function signature of the handler must be:
* @code void error_handler(
* const asio::socket_error& error // Result of operation
* ); @endcode
*/
template <typename Option, typename Error_Handler>
void set_option(const Option& option, Error_Handler error_handler)
{
service_.set_option(impl_, option, error_handler);
} }
/// Get an option from the acceptor. /// Get an option from the acceptor.
/** /**
* This function is used to get the current value of an option on the socket. * This function is used to get the current value of an option on the
* acceptor.
* *
* @param option The option value to be obtained from the socket. * @param option The option value to be obtained from the acceptor.
* *
* @throws socket_error Thrown on failure. * @throws socket_error Thrown on failure.
*/ */
template <typename Option> template <typename Option>
void get_option(Option& option) void get_option(Option& option)
{ {
service_.get_option(impl_, option); service_.get_option(impl_, option, default_error_handler());
}
/// Get an option from the acceptor.
/**
* This function is used to get the current value of an option on the
* acceptor.
*
* @param option The option value to be obtained from the acceptor.
*
* @param error_handler The handler to be called when an error occurs or when
* the function completes successfully. Copies will be made of the handler as
* required. The equivalent function signature of the handler must be:
* @code void error_handler(
* const asio::socket_error& error // Result of operation
* ); @endcode
*/
template <typename Option, typename Error_Handler>
void get_option(Option& option, Error_Handler error_handler)
{
service_.get_option(impl_, option, error_handler);
} }
/// Accept a new connection. /// Accept a new connection.
@ -226,7 +282,30 @@ public:
template <typename Stream> template <typename Stream>
void accept(Stream& peer_socket) void accept(Stream& peer_socket)
{ {
service_.accept(impl_, peer_socket.lowest_layer()); service_.accept(impl_, peer_socket.lowest_layer(),
default_error_handler());
}
/// Accept a new connection.
/**
* This function is used to accept a new connection from a peer into the
* given stream socket. The function call will block until a new connection
* has been accepted successfully or an error occurs.
*
* @param peer_socket The stream socket into which the new connection will be
* accepted.
*
* @param error_handler The handler to be called when an error occurs or when
* the function completes successfully. Copies will be made of the handler as
* required. The equivalent function signature of the handler must be:
* @code void error_handler(
* const asio::socket_error& error // Result of operation
* ); @endcode
*/
template <typename Stream, typename Error_Handler>
void accept(Stream& peer_socket, Error_Handler error_handler)
{
service_.accept(impl_, peer_socket.lowest_layer(), error_handler);
} }
/// Start an asynchronous accept. /// Start an asynchronous accept.
@ -298,7 +377,36 @@ public:
template <typename Stream, typename Address> template <typename Stream, typename Address>
void accept_address(Stream& peer_socket, Address& peer_address) void accept_address(Stream& peer_socket, Address& peer_address)
{ {
service_.accept(impl_, peer_socket.lowest_layer(), peer_address); service_.accept(impl_, peer_socket.lowest_layer(), peer_address,
default_error_handler());
}
/// Accept a new connection and obtain the address of the peer
/**
* This function is used to accept a new connection from a peer into the
* given stream socket, and additionally provide the address of the remote
* peer. The function call will block until a new connection has been
* accepted successfully or an error occurs.
*
* @param peer_socket The stream socket into which the new connection will be
* accepted.
*
* @param peer_address An address object which will receive the network
* address of the remote peer.
*
* @param error_handler The handler to be called when an error occurs or when
* the function completes successfully. Copies will be made of the handler as
* required. The equivalent function signature of the handler must be:
* @code void error_handler(
* const asio::socket_error& error // Result of operation
* ); @endcode
*/
template <typename Stream, typename Address, typename Error_Handler>
void accept_address(Stream& peer_socket, Address& peer_address,
Error_Handler error_handler)
{
service_.accept(impl_, peer_socket.lowest_layer(), peer_address,
error_handler);
} }
/// Start an asynchronous accept. /// Start an asynchronous accept.

View File

@ -58,21 +58,21 @@ public:
return demuxer_; return demuxer_;
} }
// Create a new socket acceptor implementation.
template <typename Address>
void create(impl_type& impl, const Address& address)
{
create(impl, address, SOMAXCONN);
}
// Create a new stream socket implementation. // Create a new stream socket implementation.
template <typename Address> template <typename Address, typename Error_Handler>
void create(impl_type& impl, const Address& address, int listen_queue) void create(impl_type& impl, const Address& address, int listen_queue,
Error_Handler error_handler)
{ {
if (listen_queue == 0)
listen_queue = SOMAXCONN;
socket_holder sock(socket_ops::socket(address.family(), SOCK_STREAM, socket_holder sock(socket_ops::socket(address.family(), SOCK_STREAM,
IPPROTO_TCP)); IPPROTO_TCP));
if (sock.get() == invalid_socket) if (sock.get() == invalid_socket)
throw socket_error(socket_ops::get_error()); {
error_handler(socket_error(socket_ops::get_error()));
return;
}
int reuse = 1; int reuse = 1;
socket_ops::setsockopt(sock.get(), SOL_SOCKET, SO_REUSEADDR, &reuse, socket_ops::setsockopt(sock.get(), SOL_SOCKET, SO_REUSEADDR, &reuse,
@ -80,12 +80,20 @@ public:
if (socket_ops::bind(sock.get(), address.native_address(), if (socket_ops::bind(sock.get(), address.native_address(),
address.native_size()) == socket_error_retval) address.native_size()) == socket_error_retval)
throw socket_error(socket_ops::get_error()); {
error_handler(socket_error(socket_ops::get_error()));
return;
}
if (socket_ops::listen(sock.get(), listen_queue) == socket_error_retval) if (socket_ops::listen(sock.get(), listen_queue) == socket_error_retval)
throw socket_error(socket_ops::get_error()); {
error_handler(socket_error(socket_ops::get_error()));
return;
}
impl = sock.release(); impl = sock.release();
error_handler(socket_error(socket_error::success));
} }
// Destroy a stream socket implementation. // Destroy a stream socket implementation.
@ -98,58 +106,89 @@ public:
} }
} }
// Set a socket option. Throws a socket_error exception on failure. // Set a socket option.
template <typename Option> template <typename Option, typename Error_Handler>
void set_option(impl_type& impl, const Option& option) void set_option(impl_type& impl, const Option& option,
Error_Handler error_handler)
{ {
if (socket_ops::setsockopt(impl, option.level(), option.name(), if (socket_ops::setsockopt(impl, option.level(), option.name(),
option.data(), option.size())) option.data(), option.size()))
throw socket_error(socket_ops::get_error()); {
error_handler(socket_error(socket_ops::get_error()));
return;
}
error_handler(socket_error(socket_error::success));
} }
// Set a socket option. Throws a socket_error exception on failure. // Set a socket option.
template <typename Option> template <typename Option, typename Error_Handler>
void get_option(impl_type& impl, Option& option) void get_option(impl_type& impl, Option& option, Error_Handler error_handler)
{ {
socket_len_type size = option.size(); socket_len_type size = option.size();
if (socket_ops::getsockopt(impl, option.level(), option.name(), if (socket_ops::getsockopt(impl, option.level(), option.name(),
option.data(), &size)) option.data(), &size))
throw socket_error(socket_ops::get_error()); {
error_handler(socket_error(socket_ops::get_error()));
return;
}
error_handler(socket_error(socket_error::success));
} }
// Accept a new connection. Throws a socket_error exception on failure. // Accept a new connection.
template <typename Stream_Socket_Service> template <typename Stream_Socket_Service, typename Error_Handler>
void accept(impl_type& impl, void accept(impl_type& impl,
basic_stream_socket<Stream_Socket_Service>& peer) basic_stream_socket<Stream_Socket_Service>& peer,
Error_Handler error_handler)
{ {
// We cannot accept a socket that is already open. // We cannot accept a socket that is already open.
if (peer.impl() != invalid_socket) if (peer.impl() != invalid_socket)
throw socket_error(socket_error::already_connected); {
error_handler(socket_error(socket_error::already_connected));
return;
}
socket_type new_socket = socket_ops::accept(impl, 0, 0); socket_type new_socket = socket_ops::accept(impl, 0, 0);
if (int error = socket_ops::get_error()) if (int error = socket_ops::get_error())
throw socket_error(error); {
error_handler(socket_error(error));
return;
}
peer.set_impl(new_socket); peer.set_impl(new_socket);
error_handler(socket_error(socket_error::success));
} }
// Accept a new connection. Throws a socket_error exception on failure. // Accept a new connection.
template <typename Stream_Socket_Service, typename Address> template <typename Stream_Socket_Service, typename Address,
typename Error_Handler>
void accept(impl_type& impl, void accept(impl_type& impl,
basic_stream_socket<Stream_Socket_Service>& peer, Address& peer_address) basic_stream_socket<Stream_Socket_Service>& peer, Address& peer_address,
Error_Handler error_handler)
{ {
// We cannot accept a socket that is already open. // We cannot accept a socket that is already open.
if (peer.impl() != invalid_socket) if (peer.impl() != invalid_socket)
throw socket_error(socket_error::already_connected); {
error_handler(socket_error(socket_error::already_connected));
return;
}
socket_addr_len_type addr_len = peer_address.native_size(); socket_addr_len_type addr_len = peer_address.native_size();
socket_type new_socket = socket_ops::accept(impl, socket_type new_socket = socket_ops::accept(impl,
peer_address.native_address(), &addr_len); peer_address.native_address(), &addr_len);
if (int error = socket_ops::get_error()) if (int error = socket_ops::get_error())
throw socket_error(error); {
error_handler(socket_error(error));
return;
}
peer_address.native_size(addr_len); peer_address.native_size(addr_len);
peer.set_impl(new_socket); peer.set_impl(new_socket);
error_handler(socket_error(socket_error::success));
} }
template <typename Stream_Socket_Service, typename Handler, template <typename Stream_Socket_Service, typename Handler,