changeset 506:627284574e42

Sockets: massive update - Separate Ip in Ipv4 and Ipv6, - Socket have no state, condition, action anymore (use Condition instead), - Get rid of Stream(Client|Connection|Server).
author David Demelier <markand@malikania.fr>
date Mon, 22 Feb 2016 16:09:51 +0100
parents ebe16d7afbe6
children b2b2442e3291
files modules/sockets/CMakeLists.txt modules/sockets/sockets.cpp modules/sockets/sockets.h modules/sockets/test/main.cpp
diffstat 4 files changed, 569 insertions(+), 1649 deletions(-) [+]
line wrap: on
line diff
--- a/modules/sockets/CMakeLists.txt	Wed Feb 03 14:06:18 2016 +0100
+++ b/modules/sockets/CMakeLists.txt	Mon Feb 22 16:09:51 2016 +0100
@@ -26,6 +26,7 @@
 	code_define_module(
 		NAME sockets
 		INCLUDES ${OPENSSL_INCLUDE_DIR}
+		FLAGS -DSOCKET_NO_SSL
 		LIBRARIES
 			${LIBRARIES}
 			${OPENSSL_LIBRARIES}
--- a/modules/sockets/sockets.cpp	Wed Feb 03 14:06:18 2016 +0100
+++ b/modules/sockets/sockets.cpp	Mon Feb 22 16:09:51 2016 +0100
@@ -185,30 +185,30 @@
 /* {{{ Error */
 
 Error::Error(Code code, std::string function)
-	: m_code{code}
-	, m_function{std::move(function)}
-	, m_error{error()}
+	: m_code(code)
+	, m_function(std::move(function))
+	, m_error(error())
 {
 }
 
 Error::Error(Code code, std::string function, int n)
-	: m_code{code}
-	, m_function{std::move(function)}
-	, m_error{error(n)}
+	: m_code(code)
+	, m_function(std::move(function))
+	, m_error(error(n))
 {
 }
 
 Error::Error(Code code, std::string function, std::string error)
-	: m_code{code}
-	, m_function{std::move(function)}
-	, m_error{std::move(error)}
+	: m_code(code)
+	, m_function(std::move(function))
+	, m_error(std::move(error))
 {
 }
 
 /* }}} */
 
 /*
- * Predefine addressed to be used
+ * Predefined addressed to be used
  * ------------------------------------------------------------------
  */
 
@@ -216,76 +216,84 @@
 
 namespace address {
 
-/* Default domain */
-int Ip::m_default{AF_INET};
+namespace {
 
-Ip::Ip(Type domain) noexcept
-	: m_domain(static_cast<int>(domain))
+void resolv(sockaddr *destination, const std::string host, std::uint16_t port, int domain)
 {
-	assert(m_domain == AF_INET6 || m_domain == AF_INET);
+	assert(domain == AF_INET || domain == AF_INET6);
+
+	addrinfo hints, *res;
+
+	std::memset(&hints, 0, sizeof (addrinfo));
+	hints.ai_family = domain;
+
+	auto error = getaddrinfo(host.c_str(), std::to_string(port).c_str(), &hints, &res);
+	if (error != 0)
+		throw Error(Error::System, "getaddrinfo", gai_strerror(error));
+
+	if (domain == AF_INET6)
+		std::memcpy(destination, res->ai_addr, res->ai_addrlen);
+	else
+		std::memcpy(destination, res->ai_addr, res->ai_addrlen);
+
+	freeaddrinfo(res);
+}
 
-	if (m_domain == AF_INET6) {
-		std::memset(&m_sin6, 0, sizeof (sockaddr_in6));
+} // !namespace
+
+/* {{{ Ipv4 */
+
+Ipv4::Ipv4() noexcept
+{
+	std::memset(&m_sin, 0, sizeof (sockaddr_in));
+}
+
+Ipv4::Ipv4(const std::string &host, std::uint16_t port)
+	: Ipv4()
+{
+	if (host == "*") {
+		m_sin.sin_addr.s_addr = INADDR_ANY;
+		m_sin.sin_family = AF_INET;
+		m_sin.sin_port = htons(port);
 	} else {
-		std::memset(&m_sin, 0, sizeof (sockaddr_in));
+		resolv(reinterpret_cast<sockaddr *>(&m_sin), host, port, AF_INET);
 	}
 }
 
-Ip::Ip(const std::string &host, int port, Type domain)
-	: m_domain(static_cast<int>(domain))
+Ipv4::Ipv4(const sockaddr_storage *ss, socklen_t length) noexcept
 {
-	assert(m_domain == AF_INET6 || m_domain == AF_INET);
+	std::memcpy(&m_sin, ss, length);
+}
 
-	if (host == "*") {
-		if (m_domain == AF_INET6) {
-			std::memset(&m_sin6, 0, sizeof (sockaddr_in6));
+/* }}} */
 
-			m_length = sizeof (sockaddr_in6);
-			m_sin6.sin6_addr = in6addr_any;
-			m_sin6.sin6_family = AF_INET6;
-			m_sin6.sin6_port = htons(port);
-		} else {
-			std::memset(&m_sin, 0, sizeof (sockaddr_in));
+/* {{{ Ipv6 */
 
-			m_length = sizeof (sockaddr_in);
-			m_sin.sin_addr.s_addr = INADDR_ANY;
-			m_sin.sin_family = AF_INET;
-			m_sin.sin_port = htons(port);
-		}
-	} else {
-		addrinfo hints, *res;
-
-		std::memset(&hints, 0, sizeof (addrinfo));
-		hints.ai_family = domain;
+Ipv6::Ipv6() noexcept
+{
+	std::memset(&m_sin6, 0, sizeof (sockaddr_in6));
+}
 
-		auto error = getaddrinfo(host.c_str(), std::to_string(port).c_str(), &hints, &res);
-		if (error != 0) {
-			throw Error{Error::System, "getaddrinfo", gai_strerror(error)};
-		}
-
-		if (m_domain == AF_INET6) {
-			std::memcpy(&m_sin6, res->ai_addr, res->ai_addrlen);
-		} else {
-			std::memcpy(&m_sin, res->ai_addr, res->ai_addrlen);
-		}
-
-		m_length = res->ai_addrlen;
-		freeaddrinfo(res);
+Ipv6::Ipv6(const std::string &host, std::uint16_t port)
+	: Ipv6()
+{
+	if (host == "*") {
+		m_sin6.sin6_addr = in6addr_any;
+		m_sin6.sin6_family = AF_INET6;
+		m_sin6.sin6_port = htons(port);
+	} else {
+		resolv(reinterpret_cast<sockaddr *>(&m_sin6), host, port, AF_INET6);
 	}
 }
 
-Ip::Ip(const sockaddr_storage *ss, socklen_t length) noexcept
-	: m_length{length}
-	, m_domain{ss->ss_family}
+Ipv6::Ipv6(const sockaddr_storage *ss, socklen_t length) noexcept
 {
-	assert(ss->ss_family == AF_INET6 || ss->ss_family == AF_INET);
+	std::memcpy(&m_sin6, ss, length);
+}
 
-	if (ss->ss_family == AF_INET6) {
-		std::memcpy(&m_sin6, ss, length);
-	} else if (ss->ss_family == AF_INET) {
-		std::memcpy(&m_sin, ss, length);
-	}
-}
+/* }}} */
+
+/* {{{ Local */
 
 #if !defined(_WIN32)
 
@@ -295,12 +303,11 @@
 }
 
 Local::Local(std::string path, bool rm) noexcept
-	: m_path{std::move(path)}
+	: m_path(std::move(path))
 {
 	/* Silently remove the file even if it fails */
-	if (rm) {
+	if (rm)
 		::remove(m_path.c_str());
-	}
 
 	/* Copy the path */
 	std::memset(m_sun.sun_path, 0, sizeof (m_sun.sun_path));
@@ -322,11 +329,19 @@
 
 #endif // !_WIN32
 
+/* }}} */
+
 } // !address
 
 /* }}} */
 
 /*
+ * Listener backends
+ * ------------------------------------------------------------------
+ */
+
+/* {{{ Backends */
+/*
  * Select
  * ------------------------------------------------------------------
  */
@@ -506,26 +521,22 @@
 {
 	uint32_t events = 0;
 
-	if ((condition & Condition::Readable) == Condition::Readable) {
+	if ((condition & Condition::Readable) == Condition::Readable)
 		events |= EPOLLIN;
-	}
-	if ((condition & Condition::Writable) == Condition::Writable) {
+	if ((condition & Condition::Writable) == Condition::Writable)
 		events |= EPOLLOUT;
-	}
 
 	return events;
 }
 
 Condition Epoll::toCondition(uint32_t events) const noexcept
 {
-	Condition condition{Condition::None};
+	Condition condition = Condition::None;
 
-	if ((events & EPOLLIN) || (events & EPOLLHUP)) {
+	if ((events & EPOLLIN) || (events & EPOLLHUP))
 		condition |= Condition::Readable;
-	}
-	if (events & EPOLLOUT) {
+	if (events & EPOLLOUT)
 		condition |= Condition::Writable;
-	}
 
 	return condition;
 }
@@ -539,22 +550,21 @@
 	ev.events = eflags;
 	ev.data.fd = h;
 
-	if (epoll_ctl(m_handle, op, h, &ev) < 0) {
-		throw Error{Error::System, "epoll_ctl"};
-	}
+	if (epoll_ctl(m_handle, op, h, &ev) < 0)
+		throw Error(Error::System, "epoll_ctl");
 }
 
 Epoll::Epoll()
-	: m_handle{epoll_create1(0)}
+	: m_handle(epoll_create1(0))
 {
-	if (m_handle < 0) {
+	if (m_handle < 0)
 		throw Error{Error::System, "epoll_create"};
-	}
 }
 
 Epoll::~Epoll()
 {
-	close(m_handle);
+	if (m_handle != -1)
+		close(m_handle);
 }
 
 /*
@@ -595,16 +605,13 @@
 	int ret = epoll_wait(m_handle, m_events.data(), m_events.size(), ms);
 	std::vector<ListenerStatus> result;
 
-	if (ret == 0) {
-		throw Error{Error::Timeout, "epoll_wait", TIMEOUT_MSG};
-	}
-	if (ret < 0) {
-		throw Error{Error::System, "epoll_wait"};
-	}
+	if (ret == 0)
+		throw Error(Error::Timeout, "epoll_wait", TIMEOUT_MSG);
+	if (ret < 0)
+		throw Error(Error::System, "epoll_wait");
 
-	for (int i = 0; i < ret; ++i) {
+	for (int i = 0; i < ret; ++i)
 		result.push_back(ListenerStatus{m_events[i].data.fd, toCondition(m_events[i].events)});
-	}
 
 	return result;
 }
@@ -625,14 +632,14 @@
 Kqueue::Kqueue()
 	: m_handle(kqueue())
 {
-	if (m_handle < 0) {
-		throw Error{Error::System, "kqueue"};
-	}
+	if (m_handle < 0)
+		throw Error(Error::System, "kqueue");
 }
 
 Kqueue::~Kqueue()
 {
-	close(m_handle);
+	if (m_handle != -1)
+		close(m_handle);
 }
 
 void Kqueue::update(Handle h, int filter, int kflags)
@@ -706,4 +713,6 @@
 
 /* }}} */
 
+/* }}} */
+
 } // !net
--- a/modules/sockets/sockets.h	Wed Feb 03 14:06:18 2016 +0100
+++ b/modules/sockets/sockets.h	Mon Feb 22 16:09:51 2016 +0100
@@ -161,6 +161,7 @@
 
 #include <cassert>
 #include <chrono>
+#include <cstdint>
 #include <cstdlib>
 #include <cstring>
 #include <exception>
@@ -449,7 +450,6 @@
 	Connecting,		//!< The connection is in progress
 	Connected,		//!< Connection is complete
 	Accepted,		//!< Socket has been accepted (client)
-	Accepting,		//!< The client acceptation is in progress
 	Closed,			//!< The socket has been closed
 	Disconnected,		//!< The connection was lost
 };
@@ -457,39 +457,6 @@
 /* }}} */
 
 /*
- * Action enum
- * ------------------------------------------------------------------
- *
- * Defines the pending operation.
- */
-
-/* {{{ Action */
-
-/**
- * @enum Action
- * @brief Define the current operation that must complete.
- *
- * Some operations like accept, connect, recv or send must sometimes do several round-trips to complete and the socket
- * action is set with that enumeration. The user is responsible of calling accept, connect send or recv until the
- * operation is complete.
- *
- * Note: the user must wait for the appropriate condition in Socket::condition to check if the required condition is
- * met.
- *
- * It is important to complete the operation in the correct order because protocols like Tls may require to continue
- * re-negociating when calling Socket::send or Socket::Recv.
- */
-enum class Action {
-	None,		//!< No action is required, socket is ready
-	Accept,		//!< The socket is not yet accepted, caller must call accept() again
-	Connect,	//!< The socket is not yet connected, caller must call connect() again
-	Receive,	//!< The received operation has not succeeded yet, caller must call recv() or recvfrom() again
-	Send		//!< The send operation has not succeded yet, caller must call send() or sendto() again
-};
-
-/* }}} */
-
-/*
  * Condition enum
  * ------------------------------------------------------------------
  *
@@ -627,9 +594,6 @@
 class Socket {
 private:
 	Protocol m_proto;
-	State m_state{State::Closed};
-	Action m_action{Action::None};
-	Condition m_condition{Condition::None};
 
 protected:
 	/**
@@ -649,8 +613,6 @@
 	 * @param protocol the protocol
 	 * @param iface the implementation
 	 * @throw net::Error on errors
-	 * @post state is set to Open
-	 * @post handle is not set to Invalid
 	 */
 	Socket(int domain, int type, int protocol, Protocol iface = {})
 		: m_proto(std::move(iface))
@@ -660,14 +622,10 @@
 #endif
 		m_handle = ::socket(domain, type, protocol);
 
-		if (m_handle == Invalid) {
-			throw Error{Error::System, "socket"};
-		}
+		if (m_handle == Invalid)
+			throw Error(Error::System, "socket");
 
 		m_proto.create(*this);
-		m_state = State::Open;
-
-		assert(m_handle != Invalid);
 	}
 
 	/**
@@ -680,7 +638,7 @@
 	 * @throw net::Error on errors
 	 */
 	explicit inline Socket(Protocol protocol = {}, const Address &address = {})
-		: Socket{address.domain(), protocol.type(), 0, std::move(protocol)}
+		: Socket(address.domain(), protocol.type(), 0, std::move(protocol))
 	{
 	}
 
@@ -690,23 +648,18 @@
 	 * @param handle the native descriptor
 	 * @param state specify the socket state
 	 * @param protocol the type of socket implementation
-	 * @post action is set to None
-	 * @post condition is set to None
 	 */
 	explicit inline Socket(Handle handle, State state = State::Closed, Protocol protocol = {}) noexcept
 		: m_proto(std::move(protocol))
-		, m_state{state}
-		, m_handle{handle}
+		, m_handle(handle)
 	{
-		assert(m_action == Action::None);
-		assert(m_condition == Condition::None);
 	}
 
 	/**
 	 * Create an invalid socket. Can be used when you cannot instanciate the socket immediately.
 	 */
 	explicit inline Socket(std::nullptr_t) noexcept
-		: m_handle{Invalid}
+		: m_handle(Invalid)
 	{
 	}
 
@@ -722,16 +675,10 @@
 	 */
 	inline Socket(Socket &&other) noexcept
 		: m_proto(std::move(other.m_proto))
-		, m_state{other.m_state}
-		, m_action{other.m_action}
-		, m_condition{other.m_condition}
-		, m_handle{other.m_handle}
+		, m_handle(other.m_handle)
 	{
 		/* Invalidate other */
 		other.m_handle = Invalid;
-		other.m_state = State::Closed;
-		other.m_action = Action::None;
-		other.m_condition = Condition::None;
 	}
 
 	/**
@@ -764,83 +711,31 @@
 	}
 
 	/**
-	 * Get the current socket state.
-	 *
-	 * @return the state
-	 */
-	inline State state() const noexcept
-	{
-		return m_state;
-	}
-
-	/**
-	 * Change the current socket state.
+	 * Tells if the socket is not invalid.
 	 *
-	 * @param state the new state
-	 * @warning only implementations should call this function
+	 * @return true if not invalid
 	 */
-	inline void setState(State state) noexcept
-	{
-		m_state = state;
-	}
-
-	/**
-	 * Get the pending operation.
-	 *
-	 * @return the action to complete before continuing
-	 * @note usually only needed in non-blocking sockets
-	 */
-	inline Action action() const noexcept
+	inline bool isOpen() const noexcept
 	{
-		return m_action;
-	}
-
-	/**
-	 * Change the pending operation.
-	 *
-	 * @param action the action
-	 * @warning you should not call this function yourself
-	 */
-	inline void setAction(Action action) noexcept
-	{
-		m_action = action;
-	}
-
-	/**
-	 * Get the condition to wait for.
-	 *
-	 * @return the condition
-	 */
-	inline Condition condition() const noexcept
-	{
-		return m_condition;
-	}
-
-	/**
-	 * Change the condition required.
-	 *
-	 * @param condition the condition
-	 * @warning you should not call this function yourself
-	 */
-	inline void setCondition(Condition condition) noexcept
-	{
-		m_condition = condition;
+		return m_handle != Invalid;
 	}
 
 	/**
 	 * Set an option for the socket. Wrapper of setsockopt(2).
 	 *
+	 * @pre isOpen()
 	 * @param level the setting level
 	 * @param name the name
 	 * @param arg the value
 	 * @throw net::Error on errors
 	 */
 	template <typename Argument>
-	void set(int level, int name, const Argument &arg)
+	inline void set(int level, int name, const Argument &arg)
 	{
-		if (setsockopt(m_handle, level, name, (ConstArg)&arg, sizeof (arg)) == Failure) {
-			throw Error{Error::System, "set"};
-		}
+		assert(m_handle != Invalid);
+
+		if (setsockopt(m_handle, level, name, (ConstArg)&arg, sizeof (arg)) == Failure)
+			throw Error(Error::System, "set");
 	}
 
 	/**
@@ -848,18 +743,22 @@
 	 *
 	 * The object must have `set(Socket<Address, Protocol> &) const`.
 	 *
+	 * @pre isOpen()
 	 * @param option the option
 	 * @throw net::Error on errors
 	 */
 	template <typename Option>
 	inline void set(const Option &option)
 	{
+		assert(m_handle != Invalid);
+
 		option.set(*this);
 	}
 
 	/**
 	 * Get an option for the socket. Wrapper of getsockopt(2).
 	 *
+	 * @pre isOpen()
 	 * @param level the setting level
 	 * @param name the name
 	 * @throw net::Error on errors
@@ -867,12 +766,13 @@
 	template <typename Argument>
 	Argument get(int level, int name)
 	{
+		assert(m_handle != Invalid);
+
 		Argument desired, result{};
 		socklen_t size = sizeof (result);
 
-		if (getsockopt(m_handle, level, name, (Arg)&desired, &size) == Failure) {
-			throw Error{Error::System, "get"};
-		}
+		if (getsockopt(m_handle, level, name, (Arg)&desired, &size) == Failure)
+			throw Error(Error::System, "get");
 
 		std::memcpy(&result, &desired, size);
 
@@ -885,13 +785,16 @@
 	 * The object must have `T get(Socket<Address, Protocol> &) const`, T can be any type and it is the value
 	 * returned from this function.
 	 *
+	 * @pre isOpen()
 	 * @return the same value as get() in the option
 	 * @throw net::Error on errors
 	 */
 	template <typename Option>
 	inline auto get() -> decltype(std::declval<Option>().get(*this))
 	{
-		return Option{}.get(*this);
+		assert(m_handle != Invalid);
+
+		return Option().get(*this);
 	}
 
 	/**
@@ -908,374 +811,244 @@
 	/**
 	 * Bind using a native address.
 	 *
+	 * @pre isOpen()
 	 * @param address the address
 	 * @param length the size
-	 * @pre state must not be Bound
 	 * @throw net::Error on errors
 	 */
-	void bind(const sockaddr *address, socklen_t length)
+	inline void bind(const sockaddr *address, socklen_t length)
 	{
-		assert(m_state != State::Bound);
-
-		if (::bind(m_handle, address, length) == Failure) {
-			throw Error{Error::System, "bind"};
-		}
-
-		m_state = State::Bound;
+		assert(m_handle != Invalid);
+
+		if (::bind(m_handle, address, length) == Failure)
+			throw Error(Error::System, "bind");
 	}
 
 	/**
 	 * Overload that takes an address.
 	 *
+	 * @pre isOpen()
 	 * @param address the address
 	 * @throw net::Error on errors
 	 */
 	inline void bind(const Address &address)
 	{
-		bind(address.address(), address.length());
+		assert(m_handle != Invalid);
+
+		if (::bind(m_handle, address.address(), address.length()) == Failure)
+			throw Error(Error::System, "bind");
+	}
+
+	/**
+	 * Overload that creates an address on the fly.
+	 *
+	 * @pre isOpen()
+	 * @param args the arguments to Address constructor
+	 * @throw net::Error on errors
+	 */
+	template <typename... Args>
+	inline void bind(Args&&... args)
+	{
+		assert(m_handle != Invalid);
+
+		Address address{std::forward<Args>(args)...};
+
+		if (::bind(m_handle, address.address(), address.length()) == Failure)
+			throw Error(Error::System, "bind");
 	}
 
 	/**
 	 * Listen for pending connection.
 	 *
+	 * @pre isOpen()
 	 * @param max the maximum number
-	 * @pre state must be Bound
 	 * @throw net::Error on errors
 	 */
 	inline void listen(int max = 128)
 	{
-		assert(m_state == State::Bound);
-
-		if (::listen(this->m_handle, max) == Failure) {
-			throw Error{Error::System, "listen"};
-		}
+		assert(m_handle != Invalid);
+
+		if (::listen(this->m_handle, max) == Failure)
+			throw Error(Error::System, "listen");
 	}
 
 	/**
-	 * Connect to the address.
 	 *
-	 * If connection cannot be established immediately, connect with no argument must be called again. See
-	 * the underlying protocol for more information.
-	 *
-	 * @pre state must be State::Open
+	 * @pre isOpen()
 	 * @param address the address
 	 * @param length the the address length
+	 * @param condition the condition to wait (Optional)
 	 * @throw net::Error on errors
-	 * @post state is set to State::Connecting or State::Connected
-	 * @note For non-blocking sockets, see the underlying protocol function for more details
 	 */
-	void connect(const sockaddr *address, socklen_t length)
+	inline void connect(const sockaddr *address, socklen_t length, Condition *condition = nullptr)
 	{
-		assert(m_state == State::Open);
-
-		m_action = Action::None;
-		m_condition = Condition::None;
-
-		m_proto.connect(*this, address, length);
-
-		assert((m_state == State::Connected  && m_action == Action::None    && m_condition == Condition::None) ||
-		       (m_state == State::Connecting && m_action == Action::Connect && m_condition != Condition::None));
+		assert(m_handle != Invalid);
+
+		m_proto.connect(*this, address, length, condition);
+
 	}
 
 	/**
 	 * Overloaded function.
 	 *
-	 * Effectively call connect(address.address(), address.length());
+	 * Effectively call connect(address.address(), address.length(), condition);
 	 *
+	 * @pre isOpen()
 	 * @param address the address
+	 * @param condition the condition to wait (Optional)
 	 */
-	inline void connect(const Address &address)
-	{
-		connect(address.address(), address.length());
-	}
-
-	/**
-	 * Continue the connection, only required with non-blocking sockets.
-	 *
-	 * @pre state must be State::Connecting
-	 * @throw net::Error on errors
-	 */
-	void connect()
+	inline void connect(const Address &address, Condition *condition = nullptr)
 	{
-		assert(m_state == State::Connecting);
-
-		m_action = Action::None;
-		m_condition = Condition::None;
-
-		m_proto.connect(*this);
-
-		assert((m_state == State::Connected  && m_action == Action::None    && m_condition == Condition::None) ||
-		       (m_state == State::Connecting && m_action == Action::Connect && m_condition != Condition::None));
+		assert(m_handle != Invalid);
+
+		connect(address.address(), address.length(), condition);
 	}
 
-	/**
-	 * Accept a new client. If there are no pending connection, throws an error.
-	 *
-	 * If the client cannot be accepted immediately, the client is returned and accept with no arguments
-	 * must be called on it. See the underlying protocol for more information.
-	 *
-	 * @pre state must be State::Bound
-	 * @param info the address where to store client's information (optional)
-	 * @return the new socket
-	 * @throw Error on errors
-	 * @post returned client's state is set to State::Accepting or State::Accepted
-	 * @note For non-blocking sockets, see the underlying protocol function for more details
-	 */
-	Socket<Address, Protocol> accept(Address *info)
+	inline void connect(Condition &condition)
 	{
-		assert(m_state == State::Bound);
-
-		m_action = Action::None;
-		m_condition = Condition::None;
+		m_proto.connect(*this, &condition);
+	}
+
+	Socket<Address, Protocol> accept(Address *info, Condition *condition = nullptr)
+	{
+		assert(m_handle != Invalid);
 
 		sockaddr_storage storage;
 		socklen_t length = sizeof (storage);
 
-		Socket<Address, Protocol> sc = m_proto.accept(*this, reinterpret_cast<sockaddr *>(&storage), &length);
-
-		if (info) {
+		Socket<Address, Protocol> sc = m_proto.accept(*this, reinterpret_cast<sockaddr *>(&storage), &length, condition);
+
+		if (info)
 			*info = Address{&storage, length};
-		}
-
-		/* Master do not change */
-		assert(m_state == State::Bound);
-		assert(m_action == Action::None);
-		assert(m_condition == Condition::None);
-
-		/* Client */
-		assert(
-			(sc.state() == State::Accepting && sc.action() == Action::Accept && sc.condition() != Condition::None) ||
-			(sc.state() == State::Accepted  && sc.action() == Action::None   && sc.condition() == Condition::None)
-		);
 
 		return sc;
 	}
 
-	/**
-	 * Continue the accept process on this client. This function must be called only when the socket is
-	 * ready to be readable or writable! (see condition).
-	 *
-	 * @pre state must be State::Accepting
-	 * @throw Error on errors
-	 * @post if connection is complete, state is changed to State::Accepted, action and condition are unset
-	 * @post if connection is still in progress, condition is set
-	 */
-	void accept()
+	inline void accept(Condition &condition)
 	{
-		assert(m_state == State::Accepting);
-
-		m_action = Action::None;
-		m_condition = Condition::None;
-
-		m_proto.accept(*this);
-
-		assert(
-			(m_state == State::Accepting && m_action == Action::Accept && m_condition != Condition::None) ||
-			(m_state == State::Accepted  && m_action == Action::None   && m_condition == Condition::None)
-		);
+		assert(m_handle != Invalid);
+
+		m_proto.accept(*this, &condition);
 	}
 
 	/**
 	 * Get the local name. This is a wrapper of getsockname().
 	 *
+	 * @pre isOpen()
 	 * @return the address
 	 * @throw Error on failures
 	 * @pre state() must not be State::Closed
 	 */
 	Address address() const
 	{
-		assert(m_state != State::Closed);
+		assert(m_handle != Invalid);
 
 		sockaddr_storage ss;
 		socklen_t length = sizeof (sockaddr_storage);
 
-		if (::getsockname(m_handle, (sockaddr *)&ss, &length) == Failure) {
-			throw Error{Error::System, "getsockname"};
-		}
+		if (::getsockname(m_handle, reinterpret_cast<sockaddr *>(&ss), &length) == Failure)
+			throw Error(Error::System, "getsockname");
 
 		return Address(&ss, length);
 	}
 
 	/**
-	 * Receive some data.
+	 *
 	 *
-	 * If the operation cannot be complete immediately, 0 is returned and user must call the function
-	 * again when ready. See the underlying protocol for more information.
-	 *
-	 * If action is set to Action::None and result is set to 0, disconnection occured.
-	 *
+	 * @pre isOpen()
 	 * @param data the destination buffer
 	 * @param length the buffer length
-	 * @pre action must not be Action::Send
 	 * @return the number of bytes received or 0
 	 * @throw Error on error
-	 * @note For non-blocking sockets, see the underlying protocol function for more details
 	 */
-	unsigned recv(void *data, unsigned length)
+	inline unsigned recv(void *data, unsigned length, Condition *condition = nullptr)
 	{
-		assert(m_action != Action::Send);
-
-		m_action = Action::None;
-		m_condition = Condition::None;
-
-		return m_proto.recv(*this, data, length);
+		assert(m_handle != Invalid);
+
+		return m_proto.recv(*this, data, length, condition);
 	}
 
 	/**
 	 * Overloaded function.
 	 *
+	 * @pre isOpen()
 	 * @param count the number of bytes to receive
 	 * @return the string
 	 * @throw Error on error
 	 */
-	inline std::string recv(unsigned count)
+	std::string recv(unsigned count, Condition *condition = nullptr)
 	{
+		assert(m_handle != Invalid);
+
 		std::string result;
 
 		result.resize(count);
-		auto n = recv(const_cast<char *>(result.data()), count);
+		auto n = recv(const_cast<char *>(result.data()), count, condition);
 		result.resize(n);
 
 		return result;
 	}
 
 	/**
-	 * Send some data.
+	 *
 	 *
-	 * If the operation cannot be complete immediately, 0 is returned and user must call the function
-	 * again when ready. See the underlying protocol for more information.
-	 *
+	 * @pre isOpen()
 	 * @param data the data buffer
 	 * @param length the buffer length
 	 * @return the number of bytes sent or 0
-	 * @pre action() must not be Flag::Receive
-	 * @throw Error on error
-	 * @note For non-blocking sockets, see the underlying protocol function for more details
-	 */
-	unsigned send(const void *data, unsigned length)
-	{
-		assert(m_action != Action::Receive);
-
-		m_action = Action::None;
-		m_condition = Condition::None;
-
-		return m_proto.send(*this, data, length);
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @param data the string to send
-	 * @return the number of bytes sent
 	 * @throw Error on error
 	 */
-	inline unsigned send(const std::string &data)
+	unsigned send(const void *data, unsigned length, Condition *condition = nullptr)
 	{
-		return send(data.c_str(), data.size());
+		assert(m_handle != Invalid);
+
+		return m_proto.send(*this, data, length, condition);
 	}
 
-	/**
-	 * Send data to an end point.
-	 *
-	 * If the operation cannot be complete immediately, 0 is returned and user must call the function
-	 * again when ready. See the underlying protocol for more information.
-	 *
-	 * @param data the buffer
-	 * @param length the buffer length
-	 * @param address the client address
-	 * @param addrlen the address length
-	 * @return the number of bytes sent
-	 * @throw net::Error on errors
-	 * @note For non-blocking sockets, see the underlying protocol function for more details
-	 */
-	inline unsigned sendto(const void *data, unsigned length, const sockaddr *address, socklen_t addrlen)
+	inline unsigned send(const std::string &data, Condition *condition = nullptr)
+	{
+		return m_proto.send(*this, data.c_str(), data.size(), condition);
+	}
+
+	inline unsigned sendto(const void *data, unsigned length, const sockaddr *address, socklen_t addrlen, Condition *condition = nullptr)
 	{
 		return m_proto.sendto(*this, data, length, address, addrlen);
 	}
 
-	/**
-	 * Overloaded function.
-	 *
-	 * @param data the buffer
-	 * @param length the buffer length
-	 * @param address the destination
-	 * @return the number of bytes sent
-	 * @throw net::Error on errors
-	 */
-	inline unsigned sendto(const void *data, unsigned length, const Address &address)
+	inline unsigned sendto(const void *data, unsigned length, const Address &address, Condition *condition = nullptr)
 	{
-		return sendto(data, length, address.address(), address.length());
+		return m_proto.sendto(data, length, address.address(), address.length(), condition);
 	}
 
-	/**
-	 * Overloaded function.
-	 *
-	 * @param data the data
-	 * @param address the address
-	 * @return the number of bytes sent
-	 * @throw net:;Error on errors
-	 */
-	inline unsigned sendto(const std::string &data, const Address &address)
+	inline unsigned sendto(const std::string &data, const Address &address, Condition *condition = nullptr)
 	{
-		return sendto(data.c_str(), data.length(), address);
+		return m_proto.sendto(*this, data.c_str(), data.length(), address.address(), address.length(), condition);
 	}
 
-	/**
-	 * Receive data from an end point.
-	 *
-	 * If the operation cannot be complete immediately, 0 is returned and user must call the function
-	 * again when ready. See the underlying protocol for more information.
-	 *
-	 * @param data the destination buffer
-	 * @param length the buffer length
-	 * @param address the address destination
-	 * @param addrlen the address length (in/out)
-	 * @return the number of bytes received
-	 * @throw net::Error on errors
-	 * @note For non-blocking sockets, see the underlying protocol function for more details
-	 */
-	inline unsigned recvfrom(void *data, unsigned length, sockaddr *address, socklen_t *addrlen)
+	inline unsigned recvfrom(void *data, unsigned length, sockaddr *address, socklen_t *addrlen, Condition *condition = nullptr)
 	{
 		return m_proto.recvfrom(*this, data, length, address, addrlen);
 	}
 
-	/**
-	 * Overloaded function.
-	 *
-	 * @param data the destination buffer
-	 * @param length the buffer length
-	 * @param info the address destination
-	 * @return the number of bytes received
-	 * @throw net::Error on errors
-	 */
-	inline unsigned recvfrom(void *data, unsigned length, Address *info = nullptr)
+	inline unsigned recvfrom(void *data, unsigned length, Address *info = nullptr, Condition *condition = nullptr)
 	{
 		sockaddr_storage storage;
 		socklen_t addrlen = sizeof (sockaddr_storage);
 
-		auto n = recvfrom(data, length, reinterpret_cast<sockaddr *>(&storage), &addrlen);
-
-		if (info && n != 0) {
+		auto n = m_proto.recvfrom(*this, data, length, reinterpret_cast<sockaddr *>(&storage), &addrlen, condition);
+
+		if (info && n != 0)
 			*info = Address{&storage, addrlen};
-		}
 
 		return n;
 	}
 
-	/**
-	 * Overloaded function.
-	 *
-	 * @param count the maximum number of bytes to receive
-	 * @param info the client information
-	 * @return the string
-	 * @throw net::Error on errors
-	 */
-	std::string recvfrom(unsigned count, Address *info = nullptr)
+	std::string recvfrom(unsigned count, Address *info = nullptr, Condition *condition = nullptr)
 	{
 		std::string result;
 
 		result.resize(count);
-		auto n = recvfrom(const_cast<char *>(result.data()), count, info);
+		auto n = recvfrom(const_cast<char *>(result.data()), count, info, condition);
 		result.resize(n);
 
 		return result;
@@ -1296,10 +1069,6 @@
 #endif
 			m_handle = Invalid;
 		}
-
-		m_state = State::Closed;
-		m_action = Action::None;
-		m_condition = Condition::None;
 	}
 
 	/**
@@ -1320,15 +1089,9 @@
 	{
 		m_handle = other.m_handle;
 		m_proto = std::move(other.m_proto);
-		m_state = other.m_state;
-		m_action = other.m_action;
-		m_condition = other.m_condition;
 
 		/* Invalidate other */
 		other.m_handle = Invalid;
-		other.m_state = State::Closed;
-		other.m_action = Action::None;
-		other.m_condition = Condition::None;
 
 		return *this;
 	}
@@ -1439,11 +1202,14 @@
  * @warning On Windows, it's not possible to check if the socket is blocking or not.
  */
 class SockBlockMode {
+private:
+	bool m_value;
+
 public:
-	/**
-	 * Set to false if you want non-blocking socket.
-	 */
-	bool value{false};
+	inline SockBlockMode(bool value = true) noexcept
+		: m_value(value)
+	{
+	}
 
 	/**
 	 * Set the option.
@@ -1457,25 +1223,21 @@
 #if defined(O_NONBLOCK) && !defined(_WIN32)
 		int flags;
 
-		if ((flags = fcntl(sc.handle(), F_GETFL, 0)) < 0) {
+		if ((flags = fcntl(sc.handle(), F_GETFL, 0)) < 0)
 			flags = 0;
-		}
-
-		if (value) {
+
+		if (m_value)
 			flags &= ~(O_NONBLOCK);
-		} else {
+		else
 			flags |= O_NONBLOCK;
-		}
-
-		if (fcntl(sc.handle(), F_SETFL, flags) < 0) {
-			throw Error{Error::System, "fcntl"};
-		}
+
+		if (fcntl(sc.handle(), F_SETFL, flags) < 0)
+			throw Error(Error::System, "fcntl");
 #else
-		unsigned long flags = (value) ? 0 : 1;
-
-		if (ioctlsocket(sc.handle(), FIONBIO, &flags) == Failure) {
-			throw Error{Error::System, "fcntl"};
-		}
+		unsigned long flags = (m_value) ? 0 : 1;
+
+		if (ioctlsocket(sc.handle(), FIONBIO, &flags) == Failure)
+			throw Error(Error::System, "fcntl");
 #endif
 	}
 
@@ -1491,13 +1253,12 @@
 #if defined(O_NONBLOCK) && !defined(_WIN32)
 		int flags = fcntl(sc.handle(), F_GETFL, 0);
 
-		if (flags < 0) {
-			throw Error{Error::System, "fcntl"};
-		}
+		if (flags < 0)
+			throw Error(Error::System, "fcntl");
 
 		return !(flags & O_NONBLOCK);
 #else
-		throw Error{Error::Other, "get", "Windows API cannot let you get the blocking status of a socket"};
+		throw Error(Error::Other, "get", std::strerror(ENOSYS));
 #endif
 	}
 };
@@ -1507,11 +1268,14 @@
  * @brief Reuse address, must be used before calling Socket::bind
  */
 class SockReuseAddress {
+private:
+	bool m_value;
+
 public:
-	/**
-	 * Set to true if you want to set the SOL_SOCKET/SO_REUSEADDR option.
-	 */
-	bool value{true};
+	inline SockReuseAddress(bool value = true) noexcept
+		: m_value(value)
+	{
+	}
 
 	/**
 	 * Set the option.
@@ -1522,7 +1286,7 @@
 	template <typename Address, typename Protocol>
 	inline void set(Socket<Address, Protocol> &sc) const
 	{
-		sc.set(SOL_SOCKET, SO_REUSEADDR, value ? 1 : 0);
+		sc.set(SOL_SOCKET, SO_REUSEADDR, m_value ? 1 : 0);
 	}
 
 	/**
@@ -1612,16 +1376,21 @@
 
 /* }}} */
 
+/* {{{ Options for TCP */
+
 /**
  * @class TcpNoDelay
  * @brief Set this option if you want to disable nagle's algorithm.
  */
 class TcpNoDelay {
+private:
+	bool m_value;
+
 public:
-	/**
-	 * Set to true to set TCP_NODELAY option.
-	 */
-	bool value{true};
+	inline TcpNoDelay(bool value = true) noexcept
+		: m_value(value)
+	{
+	}
 
 	/**
 	 * Set the option.
@@ -1632,7 +1401,7 @@
 	template <typename Address, typename Protocol>
 	inline void set(Socket<Address, Protocol> &sc) const
 	{
-		sc.set(IPPROTO_TCP, TCP_NODELAY, value ? 1 : 0);
+		sc.set(IPPROTO_TCP, TCP_NODELAY, m_value ? 1 : 0);
 	}
 
 	/**
@@ -1648,6 +1417,10 @@
 	}
 };
 
+/* }}} */
+
+/* {{{ Options for IPv6 */
+
 /**
  * @class Ipv6Only
  * @brief Control IPPROTO_IPV6/IPV6_V6ONLY
@@ -1656,11 +1429,14 @@
  * false or true if portability is a concern.
  */
 class Ipv6Only {
+private:
+	bool m_value;
+
 public:
-	/**
-	 * Set this to use only IPv6.
-	 */
-	bool value{true};
+	inline Ipv6Only(bool value = true) noexcept
+		: m_value(value)
+	{
+	}
 
 	/**
 	 * Set the option.
@@ -1671,7 +1447,7 @@
 	template <typename Address, typename Protocol>
 	inline void set(Socket<Address, Protocol> &sc) const
 	{
-		sc.set(IPPROTO_IPV6, IPV6_V6ONLY, value ? 1 : 0);
+		sc.set(IPPROTO_IPV6, IPV6_V6ONLY, m_value ? 1 : 0);
 	}
 
 	/**
@@ -1687,6 +1463,8 @@
 	}
 };
 
+/* }}} */
+
 } // !option
 
 /* }}} */
@@ -1695,7 +1473,8 @@
  * Predefined addressed to be used
  * ------------------------------------------------------------------
  *
- * - Ip,
+ * - Ipv4,
+ * - Ipv6,
  * - Local.
  */
 
@@ -1706,107 +1485,23 @@
  */
 namespace address {
 
-/**
- * @class Ip
- * @brief Base class for IPv6 and IPv4, you can use it if you don't know in advance if you'll use IPv6 or IPv4.
- */
-class Ip {
+class Ipv4 {
 public:
-	/**
-	 * @enum Type
-	 * @brief Type of ip address.
-	 */
-	enum Type {
-		v4 = AF_INET,		//!< AF_INET
-		v6 = AF_INET6		//!< AF_INET6
-	};
-
 private:
-	/*
-	 * Default domain when using default constructors.
-	 *
-	 * Note: AF_INET or AF_INET6, not
-	 */
-	static int m_default;
-
-	union {
-		mutable sockaddr_in m_sin;
-		mutable sockaddr_in6 m_sin6;
-	};
-
-	socklen_t m_length{0};
-	int m_domain{AF_INET};
+	sockaddr_in m_sin;
 
 public:
-	/**
-	 * Set the default domain to use when using default Ip constructor. By default, AF_INET is used.
-	 *
-	 * @pre domain must be Type::v4 or Type::v6
-	 */
-	static inline void setDefault(Type domain) noexcept
-	{
-		assert(domain == Type::v4 || domain == Type::v6);
-
-		m_default = static_cast<int>(domain);
-	}
-
-	/**
-	 * Construct using the default domain.
-	 */
-	inline Ip() noexcept
-		: Ip(static_cast<Type>(m_default))
-	{
-	}
-
-	/**
-	 * Default initialize the Ip domain.
-	 *
-	 * @pre domain must be AF_INET or AF_INET6 only
-	 * @param domain the domain (AF_INET or AF_INET6)
-	 */
-	Ip(Type domain) noexcept;
-
-	/**
-	 * Construct an address suitable for bind() or connect().
-	 *
-	 * @pre domain must be Type::v4 or Type::v6
-	 * @param domain the domain (AF_INET or AF_INET6)
-	 * @param host the host (* for any)
-	 * @param port the port number
-	 * @throw Error on errors
-	 */
-	Ip(const std::string &host, int port, Type domain = v4);
-
-	/**
-	 * Construct an address from a storage.
-	 *
-	 * @pre storage's domain must be AF_INET or AF_INET6 only
-	 * @param ss the storage
-	 * @param length the length
-	 */
-	Ip(const sockaddr_storage *ss, socklen_t length) noexcept;
-
-	/**
-	 * Get the domain (AF_INET or AF_INET6).
-	 *
-	 * @return the domain
-	 */
+	Ipv4() noexcept;
+	Ipv4(const std::string &host, std::uint16_t port);
+	Ipv4(const sockaddr_storage *ss, socklen_t length) noexcept;
+
 	inline int domain() const noexcept
 	{
-		return m_domain;
+		return AF_INET;
 	}
 
-	/**
-	 * Return the underlying address, either sockaddr_in6 or sockaddr_in.
-	 *
-	 * @return the address
-	 */
 	inline const sockaddr *address() const noexcept
 	{
-		if (m_domain == AF_INET6) {
-			return reinterpret_cast<const sockaddr *>(&m_sin6);
-		}
-
 		return reinterpret_cast<const sockaddr *>(&m_sin);
 	}
 
@@ -1817,7 +1512,7 @@
 	 */
 	inline socklen_t length() const noexcept
 	{
-		return m_length;
+		return sizeof (sockaddr_in);
 	}
 
 	/**
@@ -1825,12 +1520,8 @@
 	 *
 	 * @return the port
 	 */
-	inline int port() const noexcept
+	inline std::uint16_t port() const noexcept
 	{
-		if (m_domain == AF_INET6) {
-			return ntohs(m_sin6.sin6_port);
-		}
-
 		return ntohs(m_sin.sin_port);
 	}
 
@@ -1846,15 +1537,66 @@
 
 		std::memset(result, 0, sizeof (result));
 
-		if (m_domain == AF_INET6) {
-			if (!inet_ntop(AF_INET6, &m_sin6.sin6_addr, result, sizeof (result))) {
-				throw Error{Error::System, "inet_ntop"};
-			}
-		} else {
-			if (!inet_ntop(AF_INET, &m_sin.sin_addr, result, sizeof (result))) {
-				throw Error{Error::System, "inet_ntop"};
-			}
-		}
+		if (!inet_ntop(AF_INET, &m_sin.sin_addr, result, sizeof (result)))
+			throw Error(Error::System, "inet_ntop");
+
+		return result;
+	}
+};
+
+class Ipv6 {
+private:
+	sockaddr_in6 m_sin6;
+
+public:
+	Ipv6() noexcept;
+	Ipv6(const std::string &host, std::uint16_t port);
+	Ipv6(const sockaddr_storage *ss, socklen_t length) noexcept;
+
+	inline int domain() const noexcept
+	{
+		return AF_INET6;
+	}
+
+	inline const sockaddr *address() const noexcept
+	{
+		return reinterpret_cast<const sockaddr *>(&m_sin6);
+	}
+
+	/**
+	 * Return the underlying address length.
+	 *
+	 * @return the length
+	 */
+	inline socklen_t length() const noexcept
+	{
+		return sizeof (sockaddr_in6);
+	}
+
+	/**
+	 * Get the port.
+	 *
+	 * @return the port
+	 */
+	inline std::uint16_t port() const noexcept
+	{
+		return ntohs(m_sin6.sin6_port);
+	}
+
+	/**
+	 * Get the IP address in textual form.
+	 *
+	 * @return the address
+	 * @throw Error on errors
+	 */
+	std::string ip() const
+	{
+		char result[128];
+
+		std::memset(result, 0, sizeof (result));
+
+		if (!inet_ntop(AF_INET6, &m_sin6.sin6_addr, result, sizeof (result)))
+			throw Error(Error::System, "inet_ntop");
 
 		return result;
 	}
@@ -1985,31 +1727,12 @@
 		/* No-op */
 	}
 
-	/**
-	 * Standard connect.
-	 *
-	 * If the socket is marked non-blocking and the connection cannot be established immediately, then the
-	 * following is true:
-	 *
-	 * - state is set to State::Connecting,
-	 * - action is set to Action::Connect,
-	 * - condition is set to Condition::Writable.
-	 *
-	 * Then the user must wait until the socket is writable and call connect() with 0 arguments.
-	 *
-	 * If the socket is blocking, this function blocks until the connection is complete or an error occurs, in
-	 * that case state is either set to State::Connected or State::Disconnected but action and condition are
-	 * not set.
-	 *
-	 * @param sc the socket
-	 * @param address the address
-	 * @param length the length
-	 * @throw net::Error on errors
-	 * @note Wrapper of connect(2)
-	 */
 	template <typename Address, typename Protocol>
-	void connect(Socket<Address, Protocol> &sc, const sockaddr *address, socklen_t length)
+	void connect(Socket<Address, Protocol> &sc, const sockaddr *address, socklen_t length, Condition *condition)
 	{
+		if (condition)
+			*condition = Condition::None;
+
 		if (::connect(sc.handle(), address, length) == Failure) {
 			/*
 			 * Determine if the error comes from a non-blocking connect that cannot be
@@ -2019,118 +1742,61 @@
 			int error = WSAGetLastError();
 
 			if (error == WSAEWOULDBLOCK) {
-				sc.setState(State::Connecting);
-				sc.setAction(Action::Connect);
-				sc.setCondition(Condition::Writable);
+				if (condition)
+					*condition = Condition::Writable;
 			} else {
-				sc.setState(State::Disconnected);
-				throw Error{Error::System, "connect", error};
+				throw Error(Error::System, "connect", error);
 			}
 #else
 			if (errno == EINPROGRESS) {
-				sc.setState(State::Connecting);
-				sc.setAction(Action::Connect);
-				sc.setCondition(Condition::Writable);
+				if (condition)
+					*condition = Condition::Writable;
 			} else {
-				sc.setState(State::Disconnected);
-				throw Error{Error::System, "connect"};
+				throw Error(Error::System, "connect");
 			}
 #endif
-		} else {
-			sc.setState(State::Connected);
 		}
 	}
 
-	/**
-	 * Continue the connection. This function must only be called when the socket is ready for writing,
-	 * the user is responsible of waiting for that condition.
-	 *
-	 * This function check for SOL_SOCKET/SO_ERROR status.
-	 *
-	 * If the connection is complete, status is set to State::Connected, otherwise it is set to
-	 * State::Disconnected. In both cases, action and condition are not set.
-	 *
-	 * @param sc the socket
-	 * @throw net::Error on errors
-	 */
 	template <typename Address, typename Protocol>
-	void connect(Socket<Address, Protocol> &sc)
+	void connect(Socket<Address, Protocol> &sc, Condition *condition = nullptr)
 	{
+		if (condition)
+			*condition = Condition::None;
+
 		int error = sc.template get<int>(SOL_SOCKET, SO_ERROR);
 
-		if (error != 0) {
-			sc.setState(State::Disconnected);
-			throw Error{Error::System, "connect", error};
-		}
-
-		sc.setState(State::Connected);
+		if (error != 0)
+			throw Error(Error::System, "connect", error);
 	}
 
-	/**
-	 * Accept a clear client.
-	 *
-	 * If the socket is marked non-blocking and there are no pending connection, this function throws an
-	 * error. The user must wait that the socket is readable before calling this function.
-	 *
-	 * If the socket is blocking, this function blocks until a new client is connected or throws an error on
-	 * errors.
-	 *
-	 * If the socket is correctly returned, its state is set to State::Accepted and its action and condition
-	 * are not set.
-	 *
-	 * In any case, action and condition of this socket are not set.
-	 *
-	 * @param sc the socket
-	 * @param address the address destination
-	 * @param length the address length
-	 * @return the socket
-	 * @throw net::Error on errors
-	 * @note Wrapper of accept(2)
-	 */
 	template <typename Address, typename Protocol>
-	Socket<Address, Protocol> accept(Socket<Address, Protocol> &sc, sockaddr *address, socklen_t *length)
+	Socket<Address, Protocol> accept(Socket<Address, Protocol> &sc, sockaddr *address, socklen_t *length, Condition *condition = nullptr)
 	{
+		if (condition)
+			*condition = Condition::None;
+
 		Handle handle = ::accept(sc.handle(), address, length);
 
-		if (handle == Invalid) {
-			throw Error{Error::System, "accept"};
-		}
-
-		return Socket<Address, Protocol>{handle, State::Accepted};
+		if (handle == Invalid)
+			return Socket<Address, Protocol>();
+
+		return Socket<Address, Protocol>(handle);
 	}
 
-	/**
-	 * Continue accept.
-	 *
-	 * This function is just present for compatibility, it should never be called.
-	 */
 	template <typename Address, typename Protocol>
-	inline void accept(Socket<Address, Protocol> &) const noexcept
+	inline void accept(Socket<Address, Protocol> &, Condition *condition) const noexcept
 	{
-		/* no-op */
+		if (condition)
+			*condition = Condition::None;
 	}
 
-	/**
-	 * Receive data.
-	 *
-	 * If the socket is marked non-blocking and no data is available, 0 is returned and condition is set to
-	 * Condition::Readable. If 0 is returned and condition is not set, then the state is set to
-	 * State::Disconnected.
-	 *
-	 * If the socket is blocking, this function blocks until some data is available or if an error occurs.
-	 *
-	 * In any case, action is never set.
-	 *
-	 * @param sc the socket
-	 * @param data the destination
-	 * @param length the destination length
-	 * @return the number of bytes read
-	 * @throw Error on errors
-	 * @note Wrapper of recv(2)
-	 */
 	template <typename Address>
-	unsigned recv(Socket<Address, Tcp> &sc, void *data, unsigned length)
+	unsigned recv(Socket<Address, Tcp> &sc, void *data, unsigned length, Condition *condition)
 	{
+		if (condition)
+			*condition = Condition::None;
+
 		int nbread = ::recv(sc.handle(), (Arg)data, length, 0);
 
 		if (nbread == Failure) {
@@ -2139,46 +1805,33 @@
 
 			if (error == WSAEWOULDBLOCK) {
 				nbread = 0;
-				sc.setCondition(Condition::Readable);
+
+				if (condition)
+					condition = Condition::Readable;
 			} else {
-				sc.setState(State::Disconnected);
-				throw Error{Error::System, "recv", error};
+				throw Error(Error::System, "recv", error);
 			}
 #else
 			if (errno == EAGAIN || errno == EWOULDBLOCK) {
-				sc.setCondition(Condition::Readable);
+				nbread = 0;
+
+				if (condition)
+					*condition = Condition::Readable;
 			} else {
-				sc.setState(State::Disconnected);
-				throw Error{Error::System, "recv"};
+				throw Error(Error::System, "recv");
 			}
 #endif
-		} else if (nbread == 0) {
-			sc.setState(State::Disconnected);
 		}
 
 		return static_cast<unsigned>(nbread);
 	}
 
-	/**
-	 * Send some data.
-	 *
-	 * If the socket is marked non-blocking and the operation would block, then 0 is returned and condition is set to
-	 * Condition::Writable.
-	 *
-	 * If the socket is blocking, this function blocks until the data has been sent.
-	 *
-	 * On any other errors, this function throw net::Error.
-	 *
-	 * @param sc the socket
-	 * @param data the buffer to send
-	 * @param length the buffer length
-	 * @return the number of bytes sent
-	 * @throw net::Error on errors
-	 * @note Wrapper of send(2)
-	 */
 	template <typename Address>
-	unsigned send(Socket<Address, Tcp> &sc, const void *data, unsigned length)
+	unsigned send(Socket<Address, Tcp> &sc, const void *data, unsigned length, Condition *condition)
 	{
+		if (condition)
+			*condition = Condition::None;
+
 		int nbsent = ::send(sc.handle(), (ConstArg)data, length, 0);
 
 		if (nbsent == Failure) {
@@ -2187,18 +1840,20 @@
 
 			if (error == WSAEWOULDBLOCK) {
 				nbsent = 0;
-				sc.setCondition(Condition::Writable);
+
+				if (condition)
+					*condition = Condition::Writable;
 			} else {
-				sc.setState(State::Disconnected);
-				throw Error{Error::System, "send", error};
+				throw Error(Error::System, "send", error);
 			}
 #else
 			if (errno == EAGAIN || errno == EWOULDBLOCK) {
 				nbsent = 0;
-				sc.setCondition(Condition::Writable);
+
+				if (condition)
+					*condition = Condition::Writable;
 			} else {
-				sc.setState(State::Disconnected);
-				throw Error{Error::System, "send"};
+				throw Error(Error::System, "send");
 			}
 #endif
 		}
@@ -2238,25 +1893,12 @@
 		/* No-op */
 	}
 
-	/**
-	 * Receive data from an end point.
-	 *
-	 * If the socket is marked non-blocking and no data is available, 0 is returned and condition is set to
-	 * Condition::Readable.
-	 *
-	 * If the socket is blocking, this functions blocks until some data is available or if an error occurs.
-	 *
-	 * @param sc the socket
-	 * @param data the destination buffer
-	 * @param length the buffer length
-	 * @param address the address
-	 * @param addrlen the initial address length
-	 * @return the number of bytes received
-	 * @throw Error on error
-	 */
 	template <typename Address>
-	unsigned recvfrom(Socket<Address, Udp> &sc, void *data, unsigned length, sockaddr *address, socklen_t *addrlen)
+	unsigned recvfrom(Socket<Address, Udp> &sc, void *data, unsigned length, sockaddr *address, socklen_t *addrlen, Condition *condition)
 	{
+		if (condition)
+			*condition = Condition::Readable;
+
 		int nbread;
 
 		nbread = ::recvfrom(sc.handle(), (Arg)data, length, 0, address, addrlen);
@@ -2267,16 +1909,20 @@
 
 			if (error == WSAEWOULDBLOCK) {
 				nbread = 0;
-				sc.setCondition(Condition::Readable);
+
+				if (condition)
+					*condition = Condition::Writable;
 			} else {
-				throw Error{Error::System, "recvfrom"};
+				throw Error(Error::System, "recvfrom");
 			}
 #else
 			if (errno == EAGAIN || errno == EWOULDBLOCK) {
 				nbread = 0;
-				sc.setCondition(Condition::Readable);
+
+				if (condition)
+					*condition = Condition::Writable;
 			} else {
-				throw Error{Error::System, "recvfrom"};
+				throw Error(Error::System, "recvfrom");
 			}
 #endif
 		}
@@ -2284,25 +1930,12 @@
 		return static_cast<unsigned>(nbread);
 	}
 
-	/**
-	 * Send data to an end point.
-	 *
-	 * If the socket is marked non-blocking and the operation would block, then 0 is returned and condition is set to
-	 * Condition::Writable.
-	 *
-	 * If the socket is blocking, this functions blocks until the data has been sent.
-	 *
-	 * @param sc the socket
-	 * @param data the buffer
-	 * @param length the buffer length
-	 * @param address the client address
-	 * @param addrlen the adderss length
-	 * @return the number of bytes sent
-	 * @throw Error on error
-	 */
 	template <typename Address>
-	unsigned sendto(Socket<Address, Udp> &sc, const void *data, unsigned length, const sockaddr *address, socklen_t addrlen)
+	unsigned sendto(Socket<Address, Udp> &sc, const void *data, unsigned length, const sockaddr *address, socklen_t addrlen, Condition *condition)
 	{
+		if (condition)
+			*condition = Condition::None;
+
 		int nbsent;
 
 		nbsent = ::sendto(sc.handle(), (ConstArg)data, length, 0, address, addrlen);
@@ -2312,16 +1945,20 @@
 
 			if (error == WSAEWOULDBLOCK) {
 				nbsent = 0;
-				sc.setCondition(Condition::Writable);
+
+				if (condition)
+					*condition = Condition::Writable;
 			} else {
-				throw Error{Error::System, "sendto", error};
+				throw Error(Error::System, "sendto", error);
 			}
 #else
 			if (errno == EAGAIN || errno == EWOULDBLOCK) {
 				nbsent = 0;
-				sc.setCondition(Condition::Writable);
+
+				if (condition)
+					*condition = Condition::Writable;
 			} else {
-				throw Error{Error::System, "sendto"};
+				throw Error(Error::System, "sendto");
 			}
 #endif
 		}
@@ -2373,8 +2010,8 @@
 	 * Construct with a context and ssl, for Tls::accept.
 	 */
 	Tls(Context context, Ssl ssl)
-		: m_context{std::move(context)}
-		, m_ssl{std::move(ssl)}
+		: m_context(std::move(context))
+		, m_ssl(std::move(ssl))
 	{
 	}
 
@@ -2737,9 +2374,14 @@
 using SocketTcp = Socket<Address, protocol::Tcp>;
 
 /**
- * Helper to create TCP/IP sockets.
+ * Helper to create TCP/IPv4 sockets.
  */
-using SocketTcpIp = Socket<address::Ip, protocol::Tcp>;
+using SocketTcpIp = Socket<address::Ipv4, protocol::Tcp>;
+
+/**
+ * Helper to create TCP/IPv6 sockets.
+ */
+using SocketTcpIpv6 = Socket<address::Ipv6, protocol::Tcp>;
 
 #if !defined(_WIN32)
 
@@ -2757,9 +2399,14 @@
 using SocketUdp = Socket<Address, protocol::Udp>;
 
 /**
- * Helper to create UDP/IP sockets.
+ * Helper to create UDP/IPv4 sockets.
  */
-using SocketUdpIp = Socket<address::Ip, protocol::Udp>;
+using SocketUdpIp = Socket<address::Ipv4, protocol::Udp>;
+
+/**
+ * Helper to create UDP/IPv6 sockets.
+ */
+using SocketUdpIpv6 = Socket<address::Ipv6, protocol::Udp>;
 
 #if !defined(SOCKET_NO_SSL)
 
@@ -2779,13 +2426,13 @@
 /* }}} */
 
 /*
- * Select wrapper
+ * Listener
  * ------------------------------------------------------------------
  *
  * Wrapper for select(2) and other various implementations.
  */
 
-/* {{{ Listener */
+/* {{{ Listener stuff */
 
 /**
  * @class ListenerStatus
@@ -2806,6 +2453,10 @@
  */
 using ListenerTable = std::map<Handle, Condition>;
 
+/* {{{ Backends */
+
+/* {{{ Select */
+
 /**
  * @class Select
  * @brief Implements select(2)
@@ -2838,6 +2489,10 @@
 	}
 };
 
+/* }}} */
+
+/* {{{ Poll */
+
 #if defined(SOCKET_HAVE_POLL)
 
 /**
@@ -2881,6 +2536,10 @@
 
 #endif
 
+/* }}} */
+
+/* {{{ Epoll */
+
 #if defined(SOCKET_HAVE_EPOLL)
 
 /**
@@ -2889,13 +2548,11 @@
  */
 class Epoll {
 private:
-	int m_handle;
+	int m_handle{-1};
 	std::vector<epoll_event> m_events;
 
 	Epoll(const Epoll &) = delete;
 	Epoll &operator=(const Epoll &) = delete;
-	Epoll(const Epoll &&) = delete;
-	Epoll &operator=(const Epoll &&) = delete;
 
 	uint32_t toEpoll(Condition flags) const noexcept;
 	Condition toCondition(uint32_t events) const noexcept;
@@ -2907,6 +2564,20 @@
 	 */
 	Epoll();
 
+	inline Epoll(const Epoll &&other) noexcept
+		: m_handle(other.m_handle)
+	{
+		other.m_handle = -1;
+	}
+	
+	Epoll &operator=(const Epoll &&other)
+	{
+		m_handle = other.m_handle;
+		other.m_handle = -1;
+
+		return *this;
+	}
+
 	/**
 	 * Close the epoll instance.
 	 */
@@ -2938,6 +2609,10 @@
 
 #endif
 
+/* }}} */
+
+/* {{{ Kqueue */
+
 #if defined(SOCKET_HAVE_KQUEUE)
 
 /**
@@ -2954,8 +2629,6 @@
 
 	Kqueue(const Kqueue &) = delete;
 	Kqueue &operator=(const Kqueue &) = delete;
-	Kqueue(Kqueue &&) = delete;
-	Kqueue &operator=(Kqueue &&) = delete;
 
 	void update(Handle sc, int filter, int kflags);
 
@@ -2965,6 +2638,20 @@
 	 */
 	Kqueue();
 
+	inline Kqueue(Kqueue &&other) noexcept
+		: m_handle(other.m_handle)
+	{
+		other.m_handle = -1;
+	}
+
+	inline Kqueue &operator=(Kqueue &&other) noexcept
+	{
+		m_handle = other.m_handle;
+		other.m_handle = -1;
+
+		return *this;
+	}
+
 	/**
 	 * Destroy the kqueue instance.
 	 */
@@ -2996,6 +2683,12 @@
 
 #endif
 
+/* }}} */
+
+/* }}} */
+
+/* {{{ Listener */
+
 /**
  * @class Listener
  * @brief Synchronous multiplexing
@@ -3158,13 +2851,11 @@
 		} else {
 			/* Remove flag if already present */
 			if ((condition & Condition::Readable) == Condition::Readable &&
-			    (it->second & Condition::Readable) == Condition::Readable) {
+			    (it->second & Condition::Readable) == Condition::Readable)
 				condition &= ~(Condition::Readable);
-			}
 			if ((condition & Condition::Writable) == Condition::Writable &&
-			    (it->second & Condition::Writable) == Condition::Writable) {
+			    (it->second & Condition::Writable) == Condition::Writable)
 				condition &= ~(Condition::Writable);
-			}
 
 			/* Still need a call? */
 			if (condition != Condition::None) {
@@ -3198,13 +2889,11 @@
 		 * state.
 		 */
 		if ((condition & Condition::Readable) == Condition::Readable &&
-		    (it->second & Condition::Readable) != Condition::Readable) {
+		    (it->second & Condition::Readable) != Condition::Readable)
 			condition &= ~(Condition::Readable);
-		}
 		if ((condition & Condition::Writable) == Condition::Writable &&
-		    (it->second & Condition::Writable) != Condition::Writable) {
+		    (it->second & Condition::Writable) != Condition::Writable)
 			condition &= ~(Condition::Writable);
-		}
 
 		if (condition != Condition::None) {
 			/* Determine if it's a complete removal */
@@ -3212,11 +2901,10 @@
 
 			m_backend.unset(m_table, sc, condition, removal);
 
-			if (removal) {
+			if (removal)
 				m_table.erase(it);
-			} else {
+			else
 				it->second &= ~(condition);
-			}
 		}
 	}
 
@@ -3237,9 +2925,8 @@
 	 */
 	inline void clear()
 	{
-		while (!m_table.empty()) {
+		while (!m_table.empty())
 			remove(m_table.begin()->first);
-		}
 	}
 
 	/**
@@ -3302,785 +2989,6 @@
 
 /* }}} */
 
-/*
- * Callback
- * ------------------------------------------------------------------
- *
- * Function owner with tests.
- */
-
-/* {{{ Callback */
-
-/**
- * @class Callback
- * @brief Convenient signal owner that checks if the target is valid.
- *
- * This class also catch all errors thrown from signals to avoid interfering with our process.
- */
-template <typename... Args>
-class Callback : public std::function<void (Args...)> {
-public:
-	/**
-	 * Inherited constructors.
-	 */
-	using std::function<void (Args...)>::function;
-
-	/**
-	 * Execute the callback only if a target is set.
-	 */
-	void operator()(Args... args) const
-	{
-		if (*this) {
-			try {
-				std::function<void (Args...)>::operator()(args...);
-			} catch (...) {
-			}
-		}
-	}
-};
-
-/* }}} */
-
-/*
- * StreamConnection
- * ------------------------------------------------------------------
- *
- * Client connected on the server side.
- */
-
-/* {{{ StreamConnection */
-
-/**
- * @class StreamConnection
- * @brief Connected client on the server side.
- *
- * This object is created from StreamServer when a new client is connected, it is the higher
- * level object of sockets and completely asynchronous.
- */
-template <typename Address, typename Protocol>
-class StreamConnection {
-public:
-	/**
-	 * Called when the output has changed.
-	 */
-	using WriteHandler = Callback<>;
-
-private:
-	/* Signals */
-	WriteHandler m_onWrite;
-
-	/* Sockets and output buffer */
-	Socket<Address, Protocol> m_socket;
-	std::string m_output;
-
-public:
-	/**
-	 * Create the connection.
-	 *
-	 * @param s the socket
-	 */
-	StreamConnection(Socket<Address, Protocol> s)
-		: m_socket{std::move(s)}
-	{
-		m_socket.set(net::option::SockBlockMode{false});
-	}
-
-	/**
-	 * Access the underlying socket.
-	 *
-	 * @return the socket
-	 * @warning use with care
-	 */
-	inline Socket<Address, Protocol> &socket() noexcept
-	{
-		return m_socket;
-	}
-
-	/**
-	 * Access the current output.
-	 *
-	 * @return the output
-	 */
-	inline const std::string &output() const noexcept
-	{
-		return m_output;
-	}
-
-	/**
-	 * Overloaded function
-	 *
-	 * @return the output
-	 * @warning use with care, avoid modifying the output if you don't know what you're doing
-	 */
-	inline std::string &output() noexcept
-	{
-		return m_output;
-	}
-
-	/**
-	 * Post some data to be sent asynchronously.
-	 *
-	 * @param str the data to append
-	 */
-	inline void send(std::string str)
-	{
-		m_output += str;
-		m_onWrite();
-	}
-
-	/**
-	 * Kill the client.
-	 */
-	inline void close()
-	{
-		m_socket.close();
-	}
-
-	/**
-	 * Set the write handler, the signal is emitted when the output has changed so that the StreamServer owner
-	 * knows that there are some data to send.
-	 *
-	 * @param handler the handler
-	 * @warning you usually never need to set this yourself
-	 */
-	inline void setWriteHandler(WriteHandler handler)
-	{
-		m_onWrite = std::move(handler);
-	}
-};
-
-/* }}} */
-
-/*
- * StreamServer
- * ------------------------------------------------------------------
- *
- * Convenient stream oriented server.
- */
-
-/* {{{ StreamServer */
-
-/**
- * @class StreamServer
- * @brief Convenient stream server for TCP and TLS.
- *
- * This class does all the things for you as accepting new clients, listening for it and sending data. It works
- * asynchronously without blocking to let you control your process workflow.
- *
- * This class is not thread safe and you must not call any of the functions from different threads.
- */
-template <typename Address, typename Protocol>
-class StreamServer {
-public:
-	/**
-	 * Handler when a new client is connected.
-	 */
-	using ConnectionHandler = Callback<const std::shared_ptr<StreamConnection<Address, Protocol>> &>;
-
-	/**
-	 * Handler when a client is disconnected.
-	 */
-	using DisconnectionHandler = Callback<const std::shared_ptr<StreamConnection<Address, Protocol>> &>;
-
-	/**
-	 * Handler when data has been received from a client.
-	 */
-	using ReadHandler = Callback<const std::shared_ptr<StreamConnection<Address, Protocol>> &, const std::string &>;
-
-	/**
-	 * Handler when data has been correctly sent to a client.
-	 */
-	using WriteHandler = Callback<const std::shared_ptr<StreamConnection<Address, Protocol>> &, const std::string &>;
-
-	/**
-	 * Handler when an error occured.
-	 */
-	using ErrorHandler = Callback<const Error &>;
-
-	/**
-	 * Handler when there was a timeout.
-	 */
-	using TimeoutHandler = Callback<>;
-
-private:
-	using ClientMap = std::map<Handle, std::shared_ptr<StreamConnection<Address, Protocol>>>;
-
-	/* Signals */
-	ConnectionHandler m_onConnection;
-	DisconnectionHandler m_onDisconnection;
-	ReadHandler m_onRead;
-	WriteHandler m_onWrite;
-	ErrorHandler m_onError;
-	TimeoutHandler m_onTimeout;
-
-	/* Sockets */
-	Socket<Address, Protocol> m_master;
-	Listener<> m_listener;
-	ClientMap m_clients;
-
-	/*
-	 * Update flags depending on the required condition.
-	 */
-	void updateFlags(std::shared_ptr<StreamConnection<Address, Protocol>> &client)
-	{
-		assert(client->socket().action() != Action::None);
-
-		m_listener.remove(client->socket().handle());
-		m_listener.set(client->socket().handle(), client->socket().condition());
-	}
-
-	/*
-	 * Continue accept process.
-	 */
-	template <typename AcceptCall>
-	void processAccept(std::shared_ptr<StreamConnection<Address, Protocol>> &client, const AcceptCall &acceptFunc)
-	{
-		try {
-			/* Do the accept */
-			acceptFunc();
-
-			/* 1. First remove completely the client */
-			m_listener.remove(client->socket().handle());
-
-			/* 2. If accept is not finished, wait for the appropriate condition */
-			if (client->socket().state() == State::Accepted) {
-				/* 3. Client is accepted, notify the user */
-				m_listener.set(client->socket().handle(), Condition::Readable);
-				m_onConnection(client);
-			} else {
-				/* Operation still in progress */
-				updateFlags(client);
-			}
-		} catch (const Error &error) {
-			m_clients.erase(client->socket().handle());
-			m_listener.remove(client->socket().handle());
-			m_onError(error);
-		}
-	}
-
-	/*
-	 * Process initial accept of master socket, this is the initial accepting process. Except on errors, the
-	 * socket is stored but the user will be notified only once the socket is completely accepted.
-	 */
-	void processInitialAccept()
-	{
-		// TODO: store address too.
-		std::shared_ptr<StreamConnection<Address, Protocol>> client = std::make_shared<StreamConnection<Address, Protocol>>(m_master.accept(nullptr));
-		std::weak_ptr<StreamConnection<Address, Protocol>> ptr{client};
-
-		/* 1. Register output changed to update listener */
-		client->setWriteHandler([this, ptr] () {
-			auto client = ptr.lock();
-
-			/* Do not update the listener immediately if an action is pending */
-			if (client && client->socket().action() == Action::None && !client->output().empty()) {
-				m_listener.set(client->socket().handle(), Condition::Writable);
-			}
-		});
-
-		/* 2. Add the client */
-		m_clients.insert(std::make_pair(client->socket().handle(), client));
-
-		/*
-		 * 2. Do an initial check to set the listener flags, at this moment the socket may or not be
-		 *    completely accepted.
-		 */
-		processAccept(client, [&] () {});
-	}
-
-	/*
-	 * Read or complete the read operation.
-	 */
-	void processRead(std::shared_ptr<StreamConnection<Address, Protocol>> &client)
-	{
-		/*
-		 * Read because there is something to read or because the pending operation is
-		 * read and must complete.
-		 */
-		auto buffer = client->socket().recv(512);
-
-		/*
-		 * Now the receive operation may be completed, in that case, two possibilities:
-		 *
-		 * 1. The action is set to None (completed)
-		 * 2. The action is still not complete, update the flags
-		 */
-		if (client->socket().action() == Action::None) {
-			/* Empty mean normal disconnection */
-			if (buffer.empty()) {
-				m_listener.remove(client->socket().handle());
-				m_clients.erase(client->socket().handle());
-				m_onDisconnection(client);
-			} else {
-				/*
-				 * At this step, it is possible that we were completing a receive operation, in this
-				 * case the write flag may be removed, add it if required.
-				 */
-				if (!client->output().empty()) {
-					m_listener.set(client->socket().handle(), Condition::Writable);
-				}
-
-				m_onRead(client, buffer);
-			}
-		} else {
-			/* Operation in progress */
-			updateFlags(client);
-		}
-	}
-
-	/*
-	 * Flush the output buffer.
-	 */
-	void processWrite(std::shared_ptr<StreamConnection<Address, Protocol>> &client)
-	{
-		auto &output = client->output();
-		auto nsent = client->socket().send(output);
-
-		if (client->socket().action() == Action::None) {
-			/* 1. Create a copy of content that has been sent */
-			auto sent = output.substr(0, nsent);
-
-			/* 2. Erase the content sent */
-			output.erase(0, nsent);
-
-			/* 3. Update listener */
-			if (output.empty()) {
-				m_listener.unset(client->socket().handle(), Condition::Writable);
-			}
-
-			/* 4. Notify user */
-			m_onWrite(client, sent);
-		} else {
-			updateFlags(client);
-		}
-	}
-
-	void processSync(std::shared_ptr<StreamConnection<Address, Protocol>> &client, Condition flags)
-	{
-		try {
-			auto action = client->socket().action();
-
-			if (action == Action::Receive ||
-			    (action == Action::None && (flags & Condition::Readable) == Condition::Readable)) {
-				processRead(client);
-			} else if ((flags & Condition::Writable) == Condition::Writable) {
-				processWrite(client);
-			}
-		} catch (const Error &error) {
-			m_onDisconnection(client);
-			m_listener.remove(client->socket().handle());
-			m_clients.erase(client->socket().handle());
-		}
-	}
-
-public:
-	/**
-	 * Create a stream server with the specified address to bind.
-	 *
-	 * @param protocol the protocol (Tcp or Tls)
-	 * @param address the address to bind
-	 * @param max the max number to listen
-	 * @throw Error on errors
-	 */
-	StreamServer(Protocol protocol, const Address &address, int max = 128)
-		: m_master{std::move(protocol), address}
-	{
-		// TODO: m_onError
-		m_master.set(SOL_SOCKET, SO_REUSEADDR, 1);
-		m_master.bind(address);
-		m_master.listen(max);
-		m_listener.set(m_master.handle(), Condition::Readable);
-	}
-
-	/**
-	 * Set the connection handler, called when a new client is connected.
-	 *
-	 * @param handler the handler
-	 */
-	inline void setConnectionHandler(ConnectionHandler handler)
-	{
-		m_onConnection = std::move(handler);
-	}
-
-	/**
-	 * Set the disconnection handler, called when a client died.
-	 *
-	 * @param handler the handler
-	 */
-	inline void setDisconnectionHandler(DisconnectionHandler handler)
-	{
-		m_onDisconnection = std::move(handler);
-	}
-
-	/**
-	 * Set the receive handler, called when a client has sent something.
-	 *
-	 * @param handler the handler
-	 */
-	inline void setReadHandler(ReadHandler handler)
-	{
-		m_onRead = std::move(handler);
-	}
-
-	/**
-	 * Set the writing handler, called when some data has been sent to a client.
-	 *
-	 * @param handler the handler
-	 */
-	inline void setWriteHandler(WriteHandler handler)
-	{
-		m_onWrite = std::move(handler);
-	}
-
-	/**
-	 * Set the error handler, called when unrecoverable error has occured.
-	 *
-	 * @param handler the handler
-	 */
-	inline void setErrorHandler(ErrorHandler handler)
-	{
-		m_onError = std::move(handler);
-	}
-
-	/**
-	 * Set the timeout handler, called when the selection has timeout.
-	 *
-	 * @param handler the handler
-	 */
-	inline void setTimeoutHandler(TimeoutHandler handler)
-	{
-		m_onTimeout = std::move(handler);
-	}
-
-	/**
-	 * Poll for the next event.
-	 *
-	 * @param timeout the timeout (-1 for indefinitely)
-	 * @throw Error on errors
-	 */
-	void poll(int timeout = -1)
-	{
-		try {
-			auto st = m_listener.wait(timeout);
-
-			if (st.socket == m_master.handle()) {
-				/* New client */
-				processInitialAccept();
-			} else {
-				/* Recv / Send / Accept on a client */
-				auto client = m_clients[st.socket];
-
-				if (client->socket().state() == State::Accepted) {
-					processSync(client, st.flags);
-				} else {
-					processAccept(client, [&] () { client->socket().accept(); });
-				}
-			}
-		} catch (const Error &error) {
-			if (error.code() == Error::Timeout) {
-				m_onTimeout();
-			} else {
-				m_onError(error);
-			}
-		}
-	}
-};
-
-/* }}} */
-
-/*
- * StreamClient
- * ------------------------------------------------------------------
- */
-
-/* {{{ StreamClient */
-
-/**
- * @class StreamClient
- * @brief Client side connection to a server.
- *
- * This class is not thread safe and you must not call any of the functions from different threads.
- */
-template <typename Address, typename Protocol>
-class StreamClient {
-public:
-	/**
-	 * Handler when connection is complete.
-	 */
-	using ConnectionHandler = Callback<>;
-
-	/**
-	 * Handler when data has been received.
-	 */
-	using ReadHandler = Callback<const std::string &>;
-
-	/**
-	 * Handler when data has been sent correctly.
-	 */
-	using WriteHandler = Callback<const std::string &>;
-
-	/**
-	 * Handler when disconnected.
-	 */
-	using DisconnectionHandler = Callback<>;
-
-	/**
-	 * Handler on unrecoverable error.
-	 */
-	using ErrorHandler = Callback<const Error &>;
-
-	/**
-	 * Handler when timeout occured.
-	 */
-	using TimeoutHandler = Callback<>;
-
-private:
-	/* Signals */
-	ConnectionHandler m_onConnection;
-	ReadHandler m_onRead;
-	WriteHandler m_onWrite;
-	DisconnectionHandler m_onDisconnection;
-	ErrorHandler m_onError;
-	TimeoutHandler m_onTimeout;
-
-	/* Socket */
-	Socket<Address, Protocol> m_socket;
-	Listener<> m_listener;
-
-	/* Output buffer */
-	std::string m_output;
-
-	/*
-	 * Update the flags after an uncompleted operation. This function must only be called when the operation
-	 * has not complete (e.g. connect, recv, send).
-	 */
-	void updateFlags()
-	{
-		assert(m_socket.action() != Action::None);
-
-		m_listener.remove(m_socket.handle());
-		m_listener.set(m_socket.handle(), m_socket.condition());
-	}
-
-	/*
-	 * This is the generic connect helper, it will be used to both initiate the connection or to continue the
-	 * connection process if needed.
-	 *
-	 * Thus the template parameter is the appropriate function to call either, m_socket.connect(address) or
-	 * m_socket.connect().
-	 *
-	 * See poll() and connect() to understand.
-	 */
-	template <typename ConnectCall>
-	void processConnect(const ConnectCall &connectFunc)
-	{
-		/* Call m_socket.connect() or m_socket.connect(address) */
-		connectFunc();
-
-		/* Remove entirely */
-		m_listener.remove(m_socket.handle());
-
-		if (m_socket.state() == State::Connected) {
-			m_onConnection();
-			m_listener.set(m_socket.handle(), Condition::Readable);
-		} else {
-			/* Connection still in progress */
-			updateFlags();
-		}
-	}
-
-	/*
-	 * Receive or complete the receive command, if the command is not complete, the listener is updated
-	 * accordingly.
-	 */
-	void processRead()
-	{
-		auto received = m_socket.recv(512);
-
-		if (m_socket.action() == Action::None) {
-			/* 0 means disconnection */
-			if (received.empty()) {
-				m_onDisconnection();
-			} else {
-				/*
-				 * At this step, it is possible that we were completing a receive operation, in this
-				 * case the write flag may be removed, add it if required.
-				 */
-				if (m_output.empty()) {
-					m_listener.unset(m_socket.handle(), Condition::Writable);
-				}
-
-				m_onRead(received);
-			}
-		} else {
-			/* Receive operation in progress */
-			updateFlags();
-		}
-	}
-
-	/*
-	 * Send or complete the send command, if the command is not complete, the listener is updated
-	 * accordingly.
-	 */
-	void processWrite()
-	{
-		auto nsent = m_socket.send(m_output);
-
-		if (m_socket.action() == Action::None) {
-			/* 1. Make a copy of what has been sent */
-			auto sent = m_output.substr(0, nsent);
-
-			/* 2. Erase sent content */
-			m_output.erase(0, nsent);
-
-			/* 3. Update flags if needed */
-			if (m_output.empty()) {
-				m_listener.unset(m_socket.handle(), Condition::Writable);
-			}
-
-			/* 4. Notify user */
-			m_onWrite(sent);
-		} else {
-			/* Send operation in progress */
-			updateFlags();
-		}
-	}
-
-	/*
-	 * Receive or send.
-	 */
-	void processSync(Condition condition)
-	{
-		if ((m_socket.action() == Action::Receive) ||
-		    (m_socket.action() == Action::None && (condition & Condition::Readable) == Condition::Readable)) {
-			processRead();
-		} else {
-			processWrite();
-		}
-	}
-
-public:
-	/**
-	 * Create a client. The client is automatically marked as non-blocking.
-	 *
-	 * @param protocol the protocol (Tcp or Tls)
-	 * @param address the optional address
-	 * @throw net::Error on failures
-	 */
-	StreamClient(Protocol protocol = {}, const Address &address = {})
-		: m_socket{std::move(protocol), address}
-	{
-		m_socket.set(net::option::SockBlockMode{false});
-		m_listener.set(m_socket.handle(), Condition::Readable);
-	}
-
-	/**
-	 * Set the connection handler, called when the connection succeed.
-	 *
-	 * @param handler the handler
-	 */
-	inline void setConnectionHandler(ConnectionHandler handler)
-	{
-		m_onConnection = std::move(handler);
-	}
-
-	/**
-	 * Set the disconnection handler, called when the server closed the connection.
-	 *
-	 * @param handler the handler
-	 */
-	inline void setDisconnectionHandler(DisconnectionHandler handler)
-	{
-		m_onDisconnection = std::move(handler);
-	}
-
-	/**
-	 * Set the read handler, called when we received something.
-	 *
-	 * @param handler the handler
-	 */
-	inline void setReadHandler(ReadHandler handler)
-	{
-		m_onRead = std::move(handler);
-	}
-
-	/**
-	 * Set the write handler, called when we successfully sent data.
-	 *
-	 * @param handler the handler
-	 */
-	inline void setWriteHandler(WriteHandler handler)
-	{
-		m_onWrite = std::move(handler);
-	}
-
-	/**
-	 * Set the error handler, called when unexpected error occurs.
-	 *
-	 * @param handler the handler
-	 */
-	inline void setErrorHandler(ErrorHandler handler)
-	{
-		m_onError = std::move(handler);
-	}
-
-	/**
-	 * Connect to a server, this function may connect immediately or not in any case the connection handler
-	 * will be called when the connection completed.
-	 *
-	 * @param address the address to connect to
-	 */
-	void connect(const Address &address) noexcept
-	{
-		assert(m_socket.state() == State::Open);
-
-		processConnect([&] () { m_socket.connect(address); });
-	}
-
-	/**
-	 * Asynchronously send data to the server.
-	 *
-	 * @param str the data to append
-	 */
-	void send(std::string str)
-	{
-		m_output += str;
-
-		/* Don't update the listener if there is a pending operation */
-		if (m_socket.state() == State::Connected && m_socket.action() == Action::None && !m_output.empty()) {
-			m_listener.set(m_socket.handle(), Condition::Writable);
-		}
-	}
-
-	/**
-	 * Wait for the next event.
-	 *
-	 * @param timeout the time to wait in milliseconds
-	 * @throw Error on errors
-	 */
-	void poll(int timeout = -1) noexcept
-	{
-		try {
-			auto st = m_listener.wait(timeout);
-
-			if (m_socket.state() != State::Connected) {
-				/* Continue the connection */
-				processConnect([&] () { m_socket.connect(); });
-			} else {
-				/* Read / Write */
-				processSync(st.flags);
-			}
-		} catch (const Error &error) {
-			if (error.code() == Error::Timeout) {
-				m_onTimeout();
-			} else {
-				m_listener.remove(m_socket.handle());
-				m_onError(error);
-			}
-		}
-	}
-};
-
 /* }}} */
 
 } // !net
--- a/modules/sockets/test/main.cpp	Wed Feb 03 14:06:18 2016 +0100
+++ b/modules/sockets/test/main.cpp	Mon Feb 22 16:09:51 2016 +0100
@@ -43,10 +43,10 @@
 	SocketTcpIp s;
 
 	try {
-		s.set(option::SockReuseAddress{true});
+		s.set(option::SockReuseAddress(true));
 		ASSERT_TRUE(s.get<option::SockReuseAddress>());
 
-		s.set(option::SockReuseAddress{false});
+		s.set(option::SockReuseAddress(false));
 		ASSERT_FALSE(s.get<option::SockReuseAddress>());
 	} catch (const std::exception &ex) {
 		FAIL() << ex.what();
@@ -58,10 +58,10 @@
 	SocketTcpIp s;
 
 	try {
-		s.set(option::TcpNoDelay{true});
+		s.set(option::TcpNoDelay(true));
 		ASSERT_TRUE(s.get<option::TcpNoDelay>());
 
-		s.set(option::TcpNoDelay{false});
+		s.set(option::TcpNoDelay(false));
 		ASSERT_FALSE(s.get<option::TcpNoDelay>());
 	} catch (const std::exception &ex) {
 		FAIL() << ex.what();
@@ -70,13 +70,13 @@
 
 TEST(Options, v6only)
 {
-	SocketTcpIp s{Tcp{}, Ip{Ip::v6}};
+	SocketTcpIpv6 s;
 
 	try {
-		s.set(option::Ipv6Only{true});
+		s.set(option::Ipv6Only(true));
 		ASSERT_TRUE(s.get<option::Ipv6Only>());
 
-		s.set(option::Ipv6Only{false});
+		s.set(option::Ipv6Only(false));
 		ASSERT_FALSE(s.get<option::Ipv6Only>());
 	} catch (const std::exception &ex) {
 		FAIL() << ex.what();
@@ -89,8 +89,8 @@
 
 class TcpServerTest : public testing::Test {
 protected:
-	SocketTcp<Ip> m_server;
-	SocketTcp<Ip> m_client;
+	SocketTcp<Ipv4> m_server;
+	SocketTcp<Ipv4> m_client;
 
 	std::thread m_tserver;
 	std::thread m_tclient;
@@ -98,7 +98,7 @@
 public:
 	TcpServerTest()
 	{
-		m_server.set(SOL_SOCKET, SO_REUSEADDR, 1);
+		m_server.set(SockReuseAddress());
 	}
 
 	~TcpServerTest()
@@ -113,7 +113,7 @@
 TEST_F(TcpServerTest, connect)
 {
 	m_tserver = std::thread([this] () {
-		m_server.bind(Ip{"*", 16000});
+		m_server.bind(Ipv4("*", 16000));
 		m_server.listen();
 		m_server.accept(nullptr);
 		m_server.close();
@@ -122,7 +122,7 @@
 	std::this_thread::sleep_for(100ms);
 
 	m_tclient = std::thread([this] () {
-		m_client.connect(Ip{"127.0.0.1", 16000});
+		m_client.connect(Ipv4("127.0.0.1", 16000));
 		m_client.close();
 	});
 }
@@ -130,7 +130,7 @@
 TEST_F(TcpServerTest, io)
 {
 	m_tserver = std::thread([this] () {
-		m_server.bind(Ip{"*", 16000});
+		m_server.bind(Ipv4("*", 16000));
 		m_server.listen();
 
 		auto client = m_server.accept(nullptr);
@@ -144,7 +144,7 @@
 	std::this_thread::sleep_for(100ms);
 
 	m_tclient = std::thread([this] () {
-		m_client.connect(Ip{"127.0.0.1", 16000});
+		m_client.connect(Ipv4("127.0.0.1", 16000));
 		m_client.send("hello world");
 
 		ASSERT_EQ("hello world", m_client.recv(512));
@@ -157,8 +157,8 @@
 
 class UdpServerTest : public testing::Test {
 protected:
-	SocketUdp<Ip> m_server;
-	SocketUdp<Ip> m_client;
+	SocketUdp<Ipv4> m_server;
+	SocketUdp<Ipv4> m_client;
 
 	std::thread m_tserver;
 	std::thread m_tclient;
@@ -166,7 +166,7 @@
 public:
 	UdpServerTest()
 	{
-		m_server.set(SOL_SOCKET, SO_REUSEADDR, 1);
+		m_server.set(SockBlockMode());
 	}
 
 	~UdpServerTest()
@@ -181,8 +181,8 @@
 TEST_F(UdpServerTest, io)
 {
 	m_tserver = std::thread([this] () {
-		Ip client;
-		Ip info{"*", 16000};
+		Ipv4 client;
+		Ipv4 info("*", 16000);
 
 		m_server.bind(info);
 		auto msg = m_server.recvfrom(512, &client);
@@ -196,7 +196,7 @@
 	std::this_thread::sleep_for(100ms);
 
 	m_tclient = std::thread([this] () {
-		Ip info{"127.0.0.1", 16000};
+		Ipv4 info("127.0.0.1", 16000);
 
 		m_client.sendto("hello world", info);
 
@@ -253,7 +253,7 @@
 TEST(ListenerSet, initialAdd)
 {
 	Listener<TestBackendSet> listener;
-	Handle s{0};
+	Handle s = 0;
 
 	listener.set(s, Condition::Readable);
 
@@ -266,7 +266,7 @@
 TEST(ListenerSet, readThenWrite)
 {
 	Listener<TestBackendSet> listener;
-	Handle s{0};
+	Handle s = 0;
 
 	listener.set(s, Condition::Readable);
 	listener.set(s, Condition::Writable);
@@ -280,7 +280,7 @@
 TEST(ListenerSet, allOneShot)
 {
 	Listener<TestBackendSet> listener;
-	Handle s{0};
+	Handle s = 0;
 
 	listener.set(s, Condition::Readable | Condition::Writable);
 
@@ -293,7 +293,7 @@
 TEST(ListenerSet, readTwice)
 {
 	Listener<TestBackendSet> listener;
-	Handle s{0};
+	Handle s = 0;
 
 	listener.set(s, Condition::Readable);
 	listener.set(s, Condition::Readable);
@@ -307,7 +307,7 @@
 TEST(ListenerSet, failure)
 {
 	Listener<TestBackendSetFail> listener;
-	Handle s{0};
+	Handle s = 0;
 
 	try {
 		listener.set(s, Condition::Readable);
@@ -368,7 +368,7 @@
 TEST(ListenerUnsetRemove, unset)
 {
 	Listener<TestBackendUnset> listener;
-	Handle s{0};
+	Handle s = 0;
 
 	listener.set(s, Condition::Readable);
 	listener.unset(s, Condition::Readable);
@@ -383,7 +383,7 @@
 TEST(ListenerUnsetRemove, unsetOne)
 {
 	Listener<TestBackendUnset> listener;
-	Handle s{0};
+	Handle s = 0;
 
 	listener.set(s, Condition::Readable | Condition::Writable);
 	listener.unset(s, Condition::Readable);
@@ -398,7 +398,7 @@
 TEST(ListenerUnsetRemove, unsetAll)
 {
 	Listener<TestBackendUnset> listener;
-	Handle s{0};
+	Handle s = 0;
 
 	listener.set(s, Condition::Readable | Condition::Writable);
 	listener.unset(s, Condition::Readable);
@@ -414,7 +414,7 @@
 TEST(ListenerUnsetRemove, remove)
 {
 	Listener<TestBackendUnset> listener;
-	Handle s{0};
+	Handle s = 0;
 
 	listener.set(s, Condition::Readable | Condition::Writable);
 	listener.remove(s);
@@ -429,7 +429,7 @@
 TEST(ListenerUnsetRemove, failure)
 {
 	Listener<TestBackendUnsetFail> listener;
-	Handle s{0};
+	Handle s = 0;
 
 	listener.set(s, Condition::Readable | Condition::Writable);
 
@@ -450,8 +450,8 @@
 class ListenerTest : public testing::Test {
 protected:
 	Listener<Select> m_listener;
-	SocketTcp<Ip> m_masterTcp;
-	SocketTcp<Ip> m_clientTcp;
+	SocketTcp<Ipv4> m_masterTcp;
+	SocketTcp<Ipv4> m_clientTcp;
 
 	std::thread m_tserver;
 	std::thread m_tclient;
@@ -459,19 +459,17 @@
 public:
 	ListenerTest()
 	{
-		m_masterTcp.set(SOL_SOCKET, SO_REUSEADDR, 1);
-		m_masterTcp.bind(Ip{"*", 16000});
+		m_masterTcp.set(SockReuseAddress());
+		m_masterTcp.bind(Ipv4("*", 16000));
 		m_masterTcp.listen();
 	}
 
 	~ListenerTest()
 	{
-		if (m_tserver.joinable()) {
+		if (m_tserver.joinable())
 			m_tserver.join();
-		}
-		if (m_tclient.joinable()) {
+		if (m_tclient.joinable())
 			m_tclient.join();
-		}
 	}
 };
 
@@ -491,7 +489,7 @@
 	std::this_thread::sleep_for(100ms);
 
 	m_tclient = std::thread([this] () {
-		m_clientTcp.connect(Ip{"127.0.0.1", 16000});
+		m_clientTcp.connect(Ipv4("127.0.0.1", 16000));
 	});
 }
 
@@ -513,7 +511,7 @@
 	std::this_thread::sleep_for(100ms);
 
 	m_tclient = std::thread([this] () {
-		m_clientTcp.connect(Ip{"127.0.0.1", 16000});
+		m_clientTcp.connect(Ipv4("127.0.0.1", 16000));
 		m_clientTcp.send("hello");
 	});
 }
@@ -524,8 +522,8 @@
 
 class NonBlockingConnectTest : public testing::Test {
 protected:
-	SocketTcp<Ip> m_server;
-	SocketTcp<Ip> m_client;
+	SocketTcp<Ipv4> m_server;
+	SocketTcp<Ipv4> m_client;
 
 	std::thread m_tserver;
 	std::thread m_tclient;
@@ -533,7 +531,7 @@
 public:
 	NonBlockingConnectTest()
 	{
-		m_client.set(SockBlockMode{false});
+		m_client.set(SockBlockMode(false));
 	}
 
 	~NonBlockingConnectTest()
@@ -551,8 +549,8 @@
 
 class TcpAcceptTest : public testing::Test {
 protected:
-	SocketTcp<Ip> m_server;
-	SocketTcp<Ip> m_client;
+	SocketTcp<Ipv4> m_server;
+	SocketTcp<Ipv4> m_client;
 
 	std::thread m_tserver;
 	std::thread m_tclient;
@@ -560,8 +558,8 @@
 public:
 	TcpAcceptTest()
 	{
-		m_server.set(SOL_SOCKET, SO_REUSEADDR, 1);
-		m_server.bind(Ip{"*", 16000});
+		m_server.set(SockReuseAddress());
+		m_server.bind(Ipv4("*", 16000));
 		m_server.listen();
 	}
 
@@ -580,8 +578,8 @@
 
 class TcpRecvTest : public testing::Test {
 protected:
-	SocketTcp<Ip> m_server;
-	SocketTcp<Ip> m_client;
+	SocketTcp<Ipv4> m_server;
+	SocketTcp<Ipv4> m_client;
 
 	std::thread m_tserver;
 	std::thread m_tclient;
@@ -589,8 +587,8 @@
 public:
 	TcpRecvTest()
 	{
-		m_server.set(SOL_SOCKET, SO_REUSEADDR, 1);
-		m_server.bind(Ip{"*", 16000});
+		m_server.set(SockReuseAddress());
+		m_server.bind(Ipv4("*", 16000));
 		m_server.listen();
 	}
 
@@ -614,7 +612,7 @@
 	std::this_thread::sleep_for(100ms);
 
 	m_tclient = std::thread([this] () {
-		m_client.connect(Ip{"127.0.0.1", 16000});
+		m_client.connect(Ipv4("127.0.0.1", 16000));
 		m_client.send("hello");
 		m_client.close();
 	});
@@ -624,10 +622,12 @@
  * Socket SSL
  * -------------------------------------------------------- */
 
+#if !defined(SOCKET_NO_SSL)
+
 class TlsRecvTest : public testing::Test {
 protected:
-	SocketTls<Ip> m_server{nullptr};
-	SocketTls<Ip> m_client;
+	SocketTls<Ipv4> m_server{nullptr};
+	SocketTls<Ipv4> m_client;
 
 	std::thread m_tserver;
 	std::thread m_tclient;
@@ -641,9 +641,9 @@
 		protocol.setPrivateKey("sockets/test.key");
 		protocol.setVerify(false);
 
-		m_server = SocketTls<Ip>{std::move(protocol)};
-		m_server.set(SOL_SOCKET, SO_REUSEADDR, 1);
-		m_server.bind(Ip{"*", 16000});
+		m_server = SocketTls<Ipv4>(std::move(protocol));
+		m_server.set(SockReuseAddress());
+		m_server.bind(Ipv4("*", 16000));
 		m_server.listen();
 	}
 
@@ -672,7 +672,7 @@
 
 	m_tclient = std::thread([this] () {
 		try {
-			m_client.connect(Ip{"127.0.0.1", 16000});
+			m_client.connect(Ipv4("127.0.0.1", 16000));
 			m_client.send("hello");
 		} catch (const net::Error &ex) {
 			FAIL() << ex.function() << ": " << ex.what();
@@ -680,6 +680,8 @@
 	});
 }
 
+#endif
+
 int main(int argc, char **argv)
 {
 	testing::InitGoogleTest(&argc, argv);