view C++/modules/Socket/Socket.h @ 385:b732c4431f7d

Socket: add default constructor everywhere
author David Demelier <markand@malikania.fr>
date Tue, 23 Jun 2015 15:14:42 +0200
parents 9fd636045546
children 743b3a1c71c8
line wrap: on
line source

/*
 * Socket.h -- portable C++ socket wrappers
 *
 * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef _SOCKET_NG_H_
#define _SOCKET_NG_H_

/**
 * @file Socket.h
 * @brief Portable socket abstraction
 *
 * User may set the following variables before compiling these files:
 *
 * SOCKET_NO_WSA_INIT	- (bool) Set to false if you don't want Socket class to
 *			  automatically calls WSAStartup() when creating sockets.
 *
 *			  Otherwise, you will need to call Socket::init,
 *			  Socket::finish yourself.
 *
 * SOCKET_NO_SSL_INIT	- (bool) Set to false if you don't want OpenSSL to be
 *			  initialized when the first SocketSsl object is created.
 */

#include <cstdlib>
#include <cstring>
#include <exception>
#include <memory>
#include <string>

#if defined(_WIN32)
#  include <atomic>
#  include <cstdlib>
#  include <mutex>

#  include <WinSock2.h>
#  include <WS2tcpip.h>
#else
#  include <cerrno>

#  include <sys/ioctl.h>
#  include <sys/socket.h>
#  include <sys/types.h>

#  include <arpa/inet.h>

#  include <netinet/in.h>

#  include <fcntl.h>
#  include <netdb.h>
#  include <unistd.h>
#endif

class SocketAddress;

/* --------------------------------------------------------
 * Socket types and errors
 * -------------------------------------------------------- */

/**
 * @class SocketError
 * @brief Base class for sockets error
 */
class SocketError : public std::exception {
public:
	/**
	 * @enum Code
	 * @brief Which kind of error
	 */
	enum Code {
		WouldBlockRead,		///!< The operation would block for reading
		WouldBlockWrite,	///!< The operation would block for writing
		Timeout,		///!< The action did timeout
		System			///!< There is a system error
	};

private:
	Code m_code;
	std::string m_function;
	std::string m_error;

public:
	/**
	 * Constructor that use the last system error.
	 *
	 * @param code which kind of error
	 * @param function the function name
	 */
	SocketError(Code code, std::string function);

	/**
	 * Constructor that use the system error set by the user.
	 *
	 * @param code which kind of error
	 * @param function the function name
	 * @param error the error
	 */
	SocketError(Code code, std::string function, int error);

	/**
	 * Constructor that set the error specified by the user.
	 *
	 * @param code which kind of error
	 * @param function the function name
	 * @param error the error
	 */
	SocketError(Code code, std::string function, std::string error);

	/**
	 * Get which function has triggered the error.
	 *
	 * @return the function name (e.g connect)
	 */
	inline const std::string &function() const noexcept
	{
		return m_function;
	}

	/**
	 * The error code.
	 *
	 * @return the code
	 */
	inline Code code() const noexcept
	{
		return m_code;
	}

	/**
	 * Get the error (only the error content).
	 *
	 * @return the error
	 */
	const char *what() const noexcept
	{
		return m_error.c_str();
	}
};

/**
 * @enum SocketState
 * @brief Category of error
 */
enum class SocketState {
	Opened,				///!< Socket is opened
	Closed,				///!< Socket has been closed
	Bound,				///!< Socket is bound to address
	Connected,			///!< Socket is connected to an end point
	Disconnected,			///!< Socket is disconnected
	Timeout				///!< Timeout has occured in a waiting operation
};

/* --------------------------------------------------------
 * Generic base sockets
 * -------------------------------------------------------- */

/**
 * @class Socket
 * @brief Base socket class for socket operations
 */
class SocketAbstract {
public:
	/* {{{ Portable types */

	/*
	 * The following types are defined differently between Unix
	 * and Windows.
	 */
#if defined(_WIN32)
	/**
	 * Socket type, SOCKET.
	 */
	using Handle	= SOCKET;

	/**
	 * Argument to pass to set.
	 */
	using ConstArg	= const char *;

	/**
	 * Argument to pass to get.
	 */
	using Arg	= char *;
#else
	/**
	 * Socket type, int.
	 */
	using Handle	= int;

	/**
	 * Argument to pass to set.
	 */
	using ConstArg	= const void *;

	/**
	 * Argument to pass to get.
	 */
	using Arg	= void *;
#endif

	/* }}} */

	/* {{{ Portable constants */

	/*
	 * The following constants are defined differently from Unix
	 * to Windows.
	 */
#if defined(_WIN32)
	/**
	 * Socket creation failure or invalidation.
	 */
	static const Handle Invalid;

	/**
	 * Socket operation failure.
	 */
	static const int Error;
#else
	/**
	 * Socket creation failure or invalidation.
	 */
	static const int Invalid;

	/**
	 * Socket operation failure.
	 */
	static const int Error;
#endif

	/* }}} */

	/* {{{ Portable initialization */

	/*
	 * Initialization stuff.
	 *
	 * The function init and finish are threadsafe.
	 */
#if defined(_WIN32)
private:
	static std::mutex s_mutex;
	static std::atomic<bool> s_initialized;

public:
	/**
	 * Calls WSACleanup.
	 */
	static inline void finish() noexcept
	{
		WSACleanup();
	}

	/**
	 * Initialize using WSAStartup.
	 */
	static inline void initialize() noexcept
	{
		std::lock_guard<std::mutex> lock(s_mutex);

		if (!s_initialized) {
			s_initialized = true;

			WSADATA wsa;
			WSAStartup(MAKEWORD(2, 2), &wsa);

			/*
			 * If SOCKET_WSA_NO_INIT is not set then the user
			 * must also call finish himself.
			 */
#if !defined(SOCKET_WSA_NO_INIT)
			atexit(finish);
#endif
		}
	}
#else
public:
	/**
	 * no-op.
	 */
	static inline void initialize() noexcept {}

	/**
	 * no-op.
	 */
	static inline void finish() noexcept {}
#endif

	/* }}} */

protected:
	Handle m_handle{Invalid};			//!< The native handle
	SocketState m_state{SocketState::Opened};	//!< The socket state

public:
	/**
	 * Get the last socket system error. The error is set from errno or from
	 * WSAGetLastError on Windows.
	 *
	 * @return a string message
	 */
	static std::string syserror();

	/**
	 * Get the last system error.
	 *
	 * @param errn the error number (errno or WSAGetLastError)
	 * @return the error
	 */
	static std::string syserror(int errn);

	/**
	 * This create an invalid socket.
	 */	
	inline SocketAbstract() noexcept
		: m_handle{Invalid}
		, m_state{SocketState::Closed}
	{
	}

	/**
	 * Construct a socket with an already created descriptor.
	 *
	 * @param handle the native descriptor
	 */
	inline SocketAbstract(Handle handle) noexcept
		: m_handle{handle}
		, m_state{SocketState::Opened}
	{
	}

	/**
	 * Create a socket handle.
	 *
	 * @param domain the domain AF_*
	 * @param type the type SOCK_*
	 * @param protocol the protocol
	 * @throw SocketError on failures
	 */
	SocketAbstract(int domain, int type, int protocol);

	/**
	 * Copy constructor deleted.
	 */
	SocketAbstract(const SocketAbstract &) = delete;

	/**
	 * Transfer ownership from other to this.
	 *
	 * @param other the other socket
	 */
	SocketAbstract(SocketAbstract &&other) noexcept;

	/**
	 * Default destructor.
	 */
	virtual ~SocketAbstract();

	/**
	 * Set an option for the socket.
	 *
	 * @param level the setting level
	 * @param name the name
	 * @param arg the value
	 * @throw SocketError on error
	 */
	template <typename Argument>
	inline void set(int level, int name, const Argument &arg)
	{
#if defined(_WIN32)
		if (setsockopt(m_handle, level, name, (SocketAbstract::ConstArg)&arg, sizeof (arg)) == Error)
#else
		if (setsockopt(m_handle, level, name, (SocketAbstract::ConstArg)&arg, sizeof (arg)) < 0)
#endif
			throw SocketError{SocketError::System, "set"};
	}

	/**
	 * Get an option for the socket.
	 *
	 * @param level the setting level
	 * @param name the name
	 * @throw SocketError on error
	 */
	template <typename Argument>
	inline Argument get(int level, int name)
	{
		Argument desired, result{};
		socklen_t size = sizeof (result);

#if defined(_WIN32)
		if (getsockopt(m_handle, level, name, (SocketAbstract::Arg)&desired, &size) == Error)
#else
		if (getsockopt(m_handle, level, name, (SocketAbstract::Arg)&desired, &size) < 0)
#endif
			throw SocketError{SocketError::System, "get"};

		std::memcpy(&result, &desired, size);

		return result;
	}

	/**
	 * Get the native handle.
	 *
	 * @return the handle
	 * @warning Not portable
	 */
	inline Handle handle() const noexcept
	{
		return m_handle;
	}

	/**
	 * Get the socket state.
	 *
	 * @return
	 */
	inline SocketState state() const noexcept
	{
		return m_state;
	}

	/**
	 * Set the blocking mode, if set to false, the socket will be marked
	 * **non-blocking**.
	 *
	 * @param block set to false to mark **non-blocking**
	 * @throw SocketError on any error
	 */
	void setBlockMode(bool block);

	/**
	 * Close the socket.
	 *
	 * Automatically called from the destructor.
	 */
	virtual void close();

	/**
	 * Assignment operator forbidden.
	 *
	 * @return *this
	 */
	SocketAbstract &operator=(const SocketAbstract &) = delete;

	/**
	 * Transfer ownership from other to this. The other socket is left
	 * invalid and will not be closed.
	 *
	 * @param other the other socket
	 * @return this
	 */
	SocketAbstract &operator=(SocketAbstract &&other) noexcept;
};

/**
 * @class Socket
 * @brief Generic socket implementation
 *
 * This class can be used to bind a socket and access its address.
 *
 * @see SocketAbstractTcp
 * @see SocketAbstractUdp
 */
template <typename Address>
class Socket : public SocketAbstract {
public:
	/**
	 * Inherited constructors.
	 */
	using SocketAbstract::SocketAbstract;

	/**
	 * Default constructor.
	 */
	Socket() = default;

	/**
	 * Bind to an address.
	 *
	 * @param address the address
	 * @throw SocketError on any error
	 */
	inline void bind(const Address &address)
	{
		const auto &sa = address.address();
		const auto addrlen = address.length();

		if (::bind(m_handle, reinterpret_cast<const sockaddr *>(&sa), addrlen) == Error) {
			throw SocketError{SocketError::System, "bind"};
		}

		m_state = SocketState::Bound;
	}

	/**
	 * Get the local name. This is a wrapper of getsockname().
	 *
	 * @return the address
	 * @throw SocketError on failures
	 */
	inline Address address() const
	{
		socklen_t length;
		sockaddr_storage ss;

		if (getsockname(m_handle, (sockaddr *)&ss, &length) == Error) {
			throw SocketError{SocketError::System, "getsockname"};
		}

		return Address(ss, length);
	}
};

/* --------------------------------------------------------
 * TCP Sockets
 * -------------------------------------------------------- */

/**
 * @class SocketAbstractTcp
 * @brief Base class for TCP sockets
 * @see SocketTcp
 * @see SocketSsl
 */
template <typename Address>
class SocketAbstractTcp : public Socket<Address> {
public:
	/**
	 * Inherited constructors.
	 */
	using Socket<Address>::Socket;

	/**
	 * Default constructor.
	 */
	SocketAbstractTcp() = default;

	/**
	 * Construct a standard TCP socket. The type is automatically
	 * set to SOCK_STREAM.
	 *
	 * @param domain the domain
	 * @param protocol the protocol
	 * @throw SocketError on error
	 */
	inline SocketAbstractTcp(int domain, int protocol)
		: Socket<Address>(domain, SOCK_STREAM, protocol)
	{
	}

	/**
	 * Listen for pending connection.
	 *
	 * @param max the maximum number
	 */
	inline void listen(int max = 128)
	{
		if (::listen(Socket<Address>::m_handle, max) == SocketAbstract::Error) {
			throw SocketError{SocketError::System, "listen"};
		}
	}

	/**
	 * Receive some data.
	 *
	 * @param data the destination buffer
	 * @param length the buffer length
	 * @throw SocketError on error
	 */
	virtual unsigned recv(void *data, unsigned length) = 0;

	/**
	 * Send some data.
	 *
	 * @param data the data buffer
	 * @param length the buffer length
	 * @throw SocketError on error
	 */
	virtual unsigned send(const void *data, unsigned length) = 0;

	/**
	 * Overloaded function.
	 *
	 * @param count the number of bytes to receive
	 * @return the string
	 * @throw SocketError on error
	 */
	inline std::string recv(unsigned count)
	{
		std::string result;

		result.resize(count);
		auto n = recv(const_cast<char *>(result.data()), count);
		result.resize(n);

		return result;
	}

	/**
	 * Overloaded function.
	 *
	 * @param data the string to send
	 * @return the number of bytes sent
	 * @throw SocketError on error
	 */
	inline unsigned send(const std::string &data)
	{
		return send(data.c_str(), data.size());
	}
};

/**
 * @class SocketTcp
 * @brief Standard implementation of TCP sockets
 *
 * This class is the basic implementation of TCP sockets.
 */
template <typename Address>
class SocketTcp : public SocketAbstractTcp<Address> {
public:
	/**
	 * Inherited constructors.
	 */
	using SocketAbstractTcp<Address>::SocketAbstractTcp;

	/**
	 * Default constructor.
	 */
	SocketTcp() = default;

	/**
	 * Connect to an end point.
	 *
	 * @param address the address
	 * @throw SocketError on error
	 */
	void connect(const Address &address);

	/**
	 * Overloaded function.
	 */
	inline SocketTcp accept()
	{
		Address dummy;

		return accept(dummy);
	}

	/**
	 * Accept a clear TCP socket.
	 *
	 * @param info the client information
	 * @return the socket
	 * @throw SocketError on error
	 */
	SocketTcp accept(Address &info);

	/**
	 * @copydoc SocketAbstractTcp<Address>::recv
	 */
	using SocketAbstractTcp<Address>::recv;

	/**
	 * @copydoc SocketAbstractTcp<Address>::send
	 */
	using SocketAbstractTcp<Address>::send;

	/**
	 * @copydoc SocketAbstractTcp<Address>::recv
	 */
	unsigned recv(void *data, unsigned length) override;

	/**
	 * @copydoc SocketAbstractTcp<Address>::send
	 */
	unsigned send(const void *data, unsigned length) override;
};

template <typename Address>
void SocketTcp<Address>::connect(const Address &address)
{
	if (SocketAbstract::m_state == SocketState::Connected) {
		return;
	}

	auto &sa = address.address();
	auto addrlen = address.length();

	if (::connect(SocketAbstract::m_handle, reinterpret_cast<const sockaddr *>(&sa), addrlen) == SocketAbstract::Error) {
		/*
		 * Determine if the error comes from a non-blocking connect that cannot be
		 * accomplished yet.
		 */
#if defined(_WIN32)
		int error = WSAGetLastError();

		if (error == WSAEWOULDBLOCK) {
			throw SocketError{SocketError::WouldBlockWrite, "connect", error};
		}

		throw SocketError{SocketError::System, "connect", error};
#else
		if (errno == EINPROGRESS) {
			throw SocketError{SocketError::WouldBlockWrite, "connect"};
		}

		throw SocketError{SocketError::System, "connect"};
#endif
	}

	SocketAbstract::m_state = SocketState::Connected;
}

template <typename Address>
unsigned SocketTcp<Address>::recv(void *data, unsigned dataLen)
{
	int nbread;

	nbread = ::recv(SocketAbstract::m_handle, (SocketAbstract::Arg)data, dataLen, 0);
	if (nbread == SocketAbstract::Error) {
#if defined(_WIN32)
		int error = WSAGetLastError();

		if (error == WSAEWOULDBLOCK) {
			throw SocketError{SocketError::WouldBlockRead, "recv", error};
		}

		throw SocketError{SocketError::System, "recv", error};
#else
		if (errno == EAGAIN || errno == EWOULDBLOCK) {
			throw SocketError{SocketError::WouldBlockRead, "recv"};
		}

		throw SocketError{SocketError::System, "recv"};
#endif
	} else if (nbread == 0) {
		SocketAbstract::m_state = SocketState::Closed;
	}

	return static_cast<unsigned>(nbread);
}

template <typename Address>
unsigned SocketTcp<Address>::send(const void *data, unsigned length)
{
	int nbsent;

	nbsent = ::send(SocketAbstract::m_handle, (SocketAbstract::ConstArg)data, length, 0);
	if (nbsent == SocketAbstract::Error) {
#if defined(_WIN32)
		int error = WSAGetLastError();

		if (error == WSAEWOULDBLOCK) {
			throw SocketError{SocketError::WouldBlockWrite, "send", error};
		}

		throw SocketError{SocketError::System, "send", error};
#else
		if (errno == EAGAIN || errno == EWOULDBLOCK) {
			throw SocketError{SocketError::WouldBlockWrite, "send"};
		}

		throw SocketError{SocketError::System, "send"};
#endif
	}

	return static_cast<unsigned>(nbsent);
}

template <typename Address>
SocketTcp<Address> SocketTcp<Address>::accept(Address &info)
{
	SocketAbstract::Handle handle;

	// Store the information
	sockaddr_storage address;
	socklen_t addrlen;

	addrlen = sizeof (sockaddr_storage);
	handle = ::accept(SocketAbstract::m_handle, reinterpret_cast<sockaddr *>(&address), &addrlen);

	if (handle == SocketAbstract::Invalid) {
#if defined(_WIN32)
		int error = WSAGetLastError();

		if (error == WSAEWOULDBLOCK) {
			throw SocketError{SocketError::WouldBlockRead, "accept", error};
		}

		throw SocketError{SocketError::System, "accept", error};
#else
		if (errno == EAGAIN || errno == EWOULDBLOCK) {
			throw SocketError{SocketError::WouldBlockRead, "accept"};
		}

		throw SocketError{SocketError::System, "accept"};
#endif
	}

	info = Address{address, addrlen};

	return SocketTcp{handle};
}

/* --------------------------------------------------------
 * UDP Sockets
 * -------------------------------------------------------- */

/**
 * @class SocketAbstractUdp
 * @brief Base class for UDP sockets
 * @see SocketUdp
 */
template <typename Address>
class SocketAbstractUdp : public Socket<Address> {
public:
	/**
	 * Inherited constructors.
	 */
	using Socket<Address>::Socket;

	/**
	 * Default constructor.
	 */
	SocketAbstractUdp() = default;

	/**
	 * Construct a UDP socket. The type is automatically set to SOCK_DGRAM.
	 *
	 * @param domain the domain (e.g AF_INET)
	 * @param protocol the protocol (usually 0)
	 */
	inline SocketAbstractUdp(int domain, int protocol)
		: Socket<Address>(domain, SOCK_DGRAM, protocol)
	{
	}

	/**
	 * Overloaded function.
	 *
	 * @param data the data
	 * @param address the address
	 * @return the number of bytes sent
	 * @throw SocketError on error
	 */
	inline unsigned sendto(const std::string &data, const Address &address)
	{
		return sendto(data.c_str(), data.length(), address);
	}

	/**
	 * Overloaded function.
	 *
	 * @param data the data
	 * @param info the client information
	 * @return the string
	 * @throw SocketError on error
	 */
	inline std::string recvfrom(unsigned count, Address &info)
	{
		std::string result;

		result.resize(count);
		auto n = recvfrom(const_cast<char *>(result.data()), count, info);
		result.resize(n);

		return result;
	}

	/**
	 * Receive data from an end point.
	 *
	 * @param data the destination buffer
	 * @param length the buffer length
	 * @param info the client information
	 * @return the number of bytes received
	 * @throw SocketError on error
	 */
	virtual unsigned recvfrom(void *data, unsigned length, Address &info) = 0;

	/**
	 * Send data to an end point.
	 *
	 * @param data the buffer
	 * @param length the buffer length
	 * @param address the client address
	 * @return the number of bytes sent
	 * @throw SocketError on error
	 */
	virtual unsigned sendto(const void *data, unsigned length, const Address &address) = 0;
};

/**
 * @class SocketUdp
 * @brief Standard implementation of UDP sockets
 *
 * This class is the basic implementation of UDP sockets.
 */
template <typename Address>
class SocketUdp : public SocketAbstractUdp<Address> {
public:
	/**
	 * Inherited constructors.
	 */
	using SocketAbstractUdp<Address>::SocketAbstractUdp;

	/**
	 * Default constructor.
	 */
	SocketUdp() = default;

	/**
	 * @copydoc SocketAbstractUdp<Address>::recv
	 */
	using SocketAbstractUdp<Address>::recvfrom;

	/**
	 * @copydoc SocketAbstractUdp<Address>::send
	 */
	using SocketAbstractUdp<Address>::sendto;

	/**
	 * @copydoc SocketAbstractUdp<Address>::recvfrom
	 */
	unsigned recvfrom(void *data, unsigned length, Address &info) override;

	/**
	 * @copydoc SocketAbstractUdp<Address>::sendto
	 */
	unsigned sendto(const void *data, unsigned length, const Address &address) override;
};

template <typename Address>
unsigned SocketUdp<Address>::recvfrom(void *data, unsigned length, Address &info)
{
	int nbread;

	// Store information
	sockaddr_storage address;
	socklen_t addrlen;

	addrlen = sizeof (struct sockaddr_storage);
	nbread = ::recvfrom(SocketAbstract::m_handle, (SocketAbstract::Arg)data, length, 0, (sockaddr *)&address, &addrlen);

	info = Address{address, addrlen};

	if (nbread == SocketAbstract::Error) {
#if defined(_WIN32)
		int error = WSAGetLastError();

		if (error == WSAEWOULDBLOCK) {
			throw SocketError{SocketError::WouldBlockRead, "recvfrom", error};
		}

		throw SocketError{SocketError::System, "recvfrom", error};
#else
		if (errno == EAGAIN || errno == EWOULDBLOCK) {
			throw SocketError{SocketError::WouldBlockRead, "recvfrom"};
		}

		throw SocketError{SocketError::System, "recvfrom"};
#endif
	}

	return static_cast<unsigned>(nbread);
}

template <typename Address>
unsigned SocketUdp<Address>::sendto(const void *data, unsigned length, const Address &info)
{
	int nbsent;

	nbsent = ::sendto(SocketAbstract::m_handle, (SocketAbstract::ConstArg)data, length, 0, (const sockaddr *)&info.address(), info.length());
	if (nbsent == SocketAbstract::Error) {
#if defined(_WIN32)
		int error = WSAGetLastError();

		if (error == WSAEWOULDBLOCK) {
			throw SocketError{SocketError::WouldBlockWrite, "sendto", error};
		}

		throw SocketError{SocketError::System, "sendto", error};
#else
		if (errno == EAGAIN || errno == EWOULDBLOCK) {
			throw SocketError{SocketError::WouldBlockWrite, "sendto"};
		}

		throw SocketError{SocketError::System, "sendto"};
#endif
	}

	return static_cast<unsigned>(nbsent);
}

/**
 * Compare two sockets.
 *
 * @param s1 the first socket
 * @param s2 the second socket
 * @return true if they equals
 */
bool operator==(const SocketAbstract &s1, const SocketAbstract &s2);

/**
 * Compare two sockets.
 *
 * @param s1 the first socket
 * @param s2 the second socket
 * @return true if they are different
 */
bool operator!=(const SocketAbstract &s1, const SocketAbstract &s2);

/**
 * Compare two sockets.
 *
 * @param s1 the first socket
 * @param s2 the second socket
 * @return true if s1 < s2
 */
bool operator<(const SocketAbstract &s1, const SocketAbstract &s2);

/**
 * Compare two sockets.
 *
 * @param s1 the first socket
 * @param s2 the second socket
 * @return true if s1 > s2
 */
bool operator>(const SocketAbstract &s1, const SocketAbstract &s2);

/**
 * Compare two sockets.
 *
 * @param s1 the first socket
 * @param s2 the second socket
 * @return true if s1 <= s2
 */
bool operator<=(const SocketAbstract &s1, const SocketAbstract &s2);

/**
 * Compare two sockets.
 *
 * @param s1 the first socket
 * @param s2 the second socket
 * @return true if s1 >= s2
 */
bool operator>=(const SocketAbstract &s1, const SocketAbstract &s2);

#endif // !_SOCKET_NG_H_