diff C++/SocketListener.h @ 258:4ad3c85ab73e

Sockets: * set(), get() now take template to determine the size * recv(), recvfrom() can take a template char array to determine the size SocketListener: * Additional preferred poll method now supported * Support for both reading and writing polling
author David Demelier <markand@malikania.fr>
date Sun, 05 Oct 2014 11:00:16 +0200
parents 806dbb6011c7
children 46ccfbee84d9
line wrap: on
line diff
--- a/C++/SocketListener.h	Fri Oct 03 16:26:26 2014 +0200
+++ b/C++/SocketListener.h	Sun Oct 05 11:00:16 2014 +0200
@@ -19,17 +19,85 @@
 #ifndef _SOCKET_LISTENER_H_
 #define _SOCKET_LISTENER_H_
 
-#include <vector>
+#include <chrono>
+#include <functional>
 
 #include "Socket.h"
 
 /**
- * @class SocketTimeout
- * @brief thrown when a timeout occured
+ * @enum SocketDirection
+ * @brief The SocketDirection enum
+ *
+ * Bitmask that can be set to both reading and writing.
  */
-class SocketTimeout final : public std::exception {
-public:
-	const char *what() const noexcept override;
+enum class SocketDirection {
+	Read	= (1 << 0),		//!< only for receive
+	Write	= (1 << 1)		//!< only for sending
+};
+
+inline SocketDirection operator&(SocketDirection x, SocketDirection y)
+{
+	return static_cast<SocketDirection>(static_cast<int>(x) & static_cast<int>(y));
+}
+
+inline SocketDirection operator|(SocketDirection x, SocketDirection y)
+{
+	return static_cast<SocketDirection>(static_cast<int>(x) | static_cast<int>(y));
+}
+
+inline SocketDirection operator^(SocketDirection x, SocketDirection y)
+{
+	return static_cast<SocketDirection>(static_cast<int>(x) ^ static_cast<int>(y));
+}
+
+inline SocketDirection operator~(SocketDirection x)
+{
+	return static_cast<SocketDirection>(~static_cast<int>(x));
+}
+
+inline SocketDirection &operator&=(SocketDirection &x, SocketDirection y)
+{
+	x = x & y;
+
+	return x;
+}
+
+inline SocketDirection &operator|=(SocketDirection &x, SocketDirection y)
+{
+	x = x | y;
+
+	return x;
+}
+
+inline SocketDirection &operator^=(SocketDirection &x, SocketDirection y)
+{
+	x = x ^ y;
+
+	return x;
+}
+
+/**
+ * @enum SocketMethod
+ * @brief The SocketMethod enum
+ *
+ * Select the method of polling. It is only a preferred method, for example if you
+ * request for poll but it is not available, select will be used.
+ */
+enum class SocketMethod {
+	Select,				//!< select(2) method, fallback
+	Poll				//!< poll(2), everywhere possible
+};
+
+/**
+ * @struct SocketStatus
+ * @brief The SocketStatus struct
+ *
+ * Result of a select call, returns the first ready socket found with its
+ * direction.
+ */
+struct SocketStatus {
+	Socket		socket;		//!< which socket is ready
+	SocketDirection	direction;	//!< the direction
 };
 
 /**
@@ -39,55 +107,164 @@
  * Convenient wrapper around the select() system call.
  */
 class SocketListener final {
-private:
-	std::vector<Socket> m_sockets;
+public:
+	/**
+	 * @brief Function for listing all sockets
+	 */
+	using MapFunc	= std::function<void (Socket &, SocketDirection)>;
+
+	/**
+	 * @class Interface
+	 * @brief Implement the polling method
+	 */
+	class Interface {
+	public:
+		/**
+		 * Default destructor.
+		 */
+		virtual ~Interface() = default;
+
+		/**
+		 * List all sockets in the interface.
+		 *
+		 * @param func the function
+		 */
+		virtual void list(const MapFunc &func) = 0;
+
+		/**
+		 * Add a socket with a specified direction.
+		 *
+		 * @param s the socket
+		 * @param direction the direction
+		 */
+		virtual void add(Socket &&s, SocketDirection direction) = 0;
+
+		/**
+		 * Remove a socket with a specified direction.
+		 *
+		 * @param s the socket
+		 * @param direction the direction
+		 */
+		virtual void remove(const Socket &s, SocketDirection direction) = 0;
+
+		/**
+		 * Remove all sockets.
+		 */
+		virtual void clear() = 0;
+
+		/**
+		 * Get the total number of sockets in the listener.
+		 */
+		virtual unsigned size() const = 0;
+
+		/**
+		 * Select a socket.
+		 *
+		 * @param ms the number of milliseconds to wait, -1 means forever
+		 * @return the socket status
+		 * @throw error::Failure on failure
+		 * @throw error::Timeout on timeout
+		 */
+		virtual SocketStatus select(int ms) = 0;
+	};
+
+	std::unique_ptr<Interface> m_interface;
+
+#if defined(_WIN32)
+#  if _WIN32_WINNT >= 0x0600
+	static constexpr const SocketMethod PreferredMethod = SocketMethod::Poll;
+#  else
+	static constexpr const SocketMethod PreferredMethod = SocketMethod::Select;
+#  endif
+#else
+	static constexpr const SocketMethod PreferredMethod = SocketMethod::Poll;
+#endif
 
 public:
 	/**
-	 * Create a socket listener with a specific number of sockets to reserve.
+	 * Create a socket listener.
 	 *
-	 * @param count the number of socket to reserve (default: 0)
+	 * @param method the preferred method
 	 */
-	SocketListener(int count = 0);
+	SocketListener(SocketMethod method = SocketMethod::Poll);
 
 	/**
 	 * Add a socket to listen to.
 	 *
 	 * @param s the socket
+	 * @param direction the direction
 	 */
-	void add(Socket s);
+	inline void add(Socket s, SocketDirection direction)
+	{
+		m_interface->add(std::move(s), direction);
+	}
 
 	/**
 	 * Remove a socket from the list.
 	 *
 	 * @param s the socket
+	 * @param direction the direction
 	 */
-	void remove(const Socket &s);
+	inline void remove(const Socket &s, SocketDirection direction)
+	{
+		m_interface->remove(s, direction);
+	}
 
 	/**
 	 * Remove every sockets in the listener.
 	 */
-	void clear();
+	inline void clear()
+	{
+		m_interface->clear();
+	}
 
 	/**
 	 * Get the number of clients in listener.
 	 *
-	 * @return the number of clients in the listener.
+	 * @return the total number of sockets in the listener
 	 */
-	unsigned size();
+	inline unsigned size() const
+	{
+		return m_interface->size();
+	}
 
 	/**
-	 * Wait for an event in the socket list. If both s and us are set to 0 then
-	 * it waits indefinitely.
+	 * Select a socket. Waits for a specific amount of time specified as the duration.
 	 *
-	 * @param s the timeout in seconds
-	 * @param us the timeout in milliseconds
-	 * @see take
 	 * @return the socket ready
 	 * @throw SocketError on error
 	 * @throw SocketTimeout on timeout
 	 */
-	Socket &select(int s = 0, int us = 0);
+	template <typename Rep, typename Ratio>
+	inline SocketStatus select(const std::chrono::duration<Rep, Ratio> &duration)
+	{
+		auto cvt = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
+
+		return m_interface->select(cvt.count());
+	}
+
+	/**
+	 * Overload that waits indefinitely.
+	 *
+	 * @return the socket ready
+	 * @throw SocketError on error
+	 * @throw SocketTimeout on timeout
+	 */
+	inline SocketStatus select()
+	{
+		return m_interface->select(-1);
+	}
+
+	/**
+	 * List every socket in the listener.
+	 *
+	 * @param func the function to call
+	 */
+	template <typename Func>
+	inline void list(Func func)
+	{
+		m_interface->list(func);
+	}
 };
 
 #endif // !_SOCKET_LISTENER_H_