changeset 443:9c85d9158990

Socket: constructor can now take an address for genericity with Ipv4 and Ipv6
author David Demelier <markand@malikania.fr>
date Fri, 23 Oct 2015 10:00:47 +0200
parents 44887104242a
children fc055d2a4a2c
files C++/modules/Socket/Sockets.cpp C++/modules/Socket/Sockets.h
diffstat 2 files changed, 203 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- a/C++/modules/Socket/Sockets.cpp	Fri Oct 23 08:31:46 2015 +0200
+++ b/C++/modules/Socket/Sockets.cpp	Fri Oct 23 10:00:47 2015 +0200
@@ -176,7 +176,7 @@
 	}
 }
 
-Ip::Ip(const std::string &host, int port, int domain)
+Ip::Ip(int domain, const std::string &host, int port)
 	: m_domain{domain}
 {
 	if (host == "*") {
--- a/C++/modules/Socket/Sockets.h	Fri Oct 23 08:31:46 2015 +0200
+++ b/C++/modules/Socket/Sockets.h	Fri Oct 23 10:00:47 2015 +0200
@@ -133,6 +133,7 @@
 #include <cstdlib>
 #include <cstring>
 #include <exception>
+#include <functional>
 #include <map>
 #include <memory>
 #include <string>
@@ -411,10 +412,11 @@
 	/**
 	 * This tries to create a socket.
 	 *
+	 * @param address which type of address
 	 * @param type the type instance
 	 */
-	inline Socket(Type type = Type{}) noexcept
-		: Socket{Address::domain(), Type::type(), 0}
+	explicit inline Socket(const Address &address = {}, Type type = Type{}) noexcept
+		: Socket{address.domain(), Type::type(), 0}
 	{
 		/* Some implementation requires more things */
 		m_type = std::move(type);
@@ -933,14 +935,23 @@
 	socklen_t m_length{0};
 	int m_domain{AF_INET};
 
-	Ip(const std::string &host, int port, int domain);
 public:
 	/**
 	 * Default initialize the Ip domain.
 	 *
 	 * @param domain the domain (AF_INET or AF_INET6)
 	 */
-	Ip(int domain = AF_INET) noexcept;
+	Ip(int domain = AF_INET6) noexcept;
+
+	/**
+	 * Construct an address suitable for bind() or connect().
+	 *
+	 * @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(int domain, const std::string &host, int port);
 
 	/**
 	 * Construct an address from a storage.
@@ -951,6 +962,16 @@
 	Ip(const struct sockaddr_storage *ss, socklen_t length);
 
 	/**
+	 * Get the domain (AF_INET or AF_INET6).
+	 *
+	 * @return the domain
+	 */
+	inline int domain() const noexcept
+	{
+		return m_domain;
+	}
+
+	/**
 	 * Return the underlying address, either sockaddr_in6 or sockaddr_in.
 	 *
 	 * @return the address
@@ -982,16 +1003,6 @@
 class Ipv6 : public Ip {
 public:
 	/**
-	 * Get the domain AF_INET6.
-	 *
-	 * @return AF_INET6
-	 */
-	static inline int domain() noexcept
-	{
-		return AF_INET6;
-	}
-
-	/**
 	 * Construct an empty address.
 	 */
 	inline Ipv6() noexcept
@@ -1007,7 +1018,18 @@
 	 * @throw Error on errors
 	 */
 	inline Ipv6(const std::string &host, int port)
-		: Ip{host, port, AF_INET6}
+		: Ip{AF_INET6, host, port}
+	{
+	}
+
+	/**
+	 * Construct an address from a storage.
+	 *
+	 * @param ss the storage
+	 * @param length the length
+	 */
+	inline Ipv6(const struct sockaddr_storage *ss, socklen_t length)
+		: Ip{ss, length}
 	{
 	}
 };
@@ -1019,16 +1041,6 @@
 class Ipv4 : public Ip {
 public:
 	/**
-	 * Get the domain AF_INET.
-	 *
-	 * @return AF_INET
-	 */
-	static inline int domain() noexcept
-	{
-		return AF_INET;
-	}
-
-	/**
 	 * Construct an empty address.
 	 */
 	inline Ipv4() noexcept
@@ -1044,7 +1056,18 @@
 	 * @throw Error on errors
 	 */
 	inline Ipv4(const std::string &host, int port)
-		: Ip{host, port, AF_INET}
+		: Ip{AF_INET, host, port}
+	{
+	}
+
+	/**
+	 * Construct an address from a storage.
+	 *
+	 * @param ss the storage
+	 * @param length the length
+	 */
+	inline Ipv4(const struct sockaddr_storage *ss, socklen_t length)
+		: Ip{ss, length}
 	{
 	}
 };
@@ -1068,7 +1091,7 @@
 	 *
 	 * @return AF_LOCAL
 	 */
-	static inline int domain() noexcept
+	inline int domain() noexcept
 	{
 		return AF_LOCAL;
 	}
@@ -2198,6 +2221,158 @@
 
 /* }}} */
 
+#if 0
+
+/*
+ * High level helpers coming soon.
+ */
+
+template <typename Address, typename Type>
+class StreamServer;
+
+template <typename Address, typename Type>
+class StreamConnection {
+private:
+	Socket<Address, Type> m_socket;
+	std::string m_output;
+
+public:
+	StreamConnection(Socket<Address, Type> s)
+		: m_socket{std::move(s)}
+	{
+	}
+
+	inline Socket<Address, Type> &socket() noexcept
+	{
+		return m_socket;
+	}
+
+	const std::string &output() const noexcept
+	{
+		return m_output;
+	}
+};
+
+template <typename Address, typename Type>
+class StreamServer {
+private:
+	Socket<Address, Type> m_master;
+	Listener<> m_listener;
+	std::map<Handle, StreamConnection<Address, Type>> m_clients;
+
+	/* Callbacks */
+	template <typename... Args>
+	using Callbacks = std::vector<std::function<void (Args...)>>;
+
+	Callbacks<StreamConnection<Address, Type> &> m_onConnection;
+	Callbacks<StreamConnection<Address, Type> &> m_onDisconnection;
+	Callbacks<StreamConnection<Address, Type> &, const std::string &> m_onRead;
+	Callbacks<StreamConnection<Address, Type> &, const std::string &> m_onWrite;
+
+	template <typename List, typename... Args>
+	void notify(const List &list, Args&&... args)
+	{
+		for (const auto &f : list) {
+			f(std::forward<Args>(args)...);
+		}
+	}
+
+	void processAccept()
+	{
+		StreamConnection<Address, Type> connection{m_master.accept()};
+
+		/* Notify we have a new client */
+		notify(m_onConnection, connection);
+
+		m_listener.set(connection.socket().handle(), FlagRead);
+		m_clients.emplace(connection.socket().handle(), std::move(connection));
+	}
+
+	void processSync(const ListenerStatus &status)
+	{
+		auto &connection = m_clients.at(status.socket);
+
+		try {
+			if (status.flags & FlagRead) {
+				auto buffer = connection.socket().recv(512);
+
+				/* Empty mean normal disconnection */
+				if (buffer.empty()) {
+					notify(m_onDisconnection, connection);
+					m_listener.remove(status.socket);
+					m_clients.erase(status.socket);
+				} else {
+					notify(m_onRead, connection, buffer);
+				}
+			} else if (status.flags & FlagWrite) {
+				/* Process write */
+			}
+		} catch (const std::exception &ex) {
+			/* TODO: remove */
+		}
+	}
+
+public:
+	/**
+	 * Create a stream server with the specified address to bind.
+	 *
+	 * @param address the address to bind
+	 * @param max the max number to listen
+	 * @throw Error on errors
+	 */
+	template <typename RealAddress>
+	StreamServer(const RealAddress &address, int max = 128)
+		: m_master{address.domain(), SOCK_STREAM, 0}
+	{
+		m_master.set(SOL_SOCKET, SO_REUSEADDR, 1);
+		m_master.bind(address);
+		m_master.listen(max);
+		m_listener.set(m_master.handle(), FlagRead);
+	}
+
+	template <typename Func>
+	inline void addConnectionHandler(Func &&f)
+	{
+		m_onConnection.push_back(std::forward<Func>(f));
+	}
+
+	template <typename Func>
+	inline void addDisconnectionHandler(Func &&f)
+	{
+		m_onDisconnection.push_back(std::forward<Func>(f));
+	}
+
+	template <typename Func>
+	inline void addReadHandler(Func &&f)
+	{
+		m_onRead.push_back(std::forward<Func>(f));
+	}
+
+	template <typename Func>
+	inline void addWriteHandler(Func &&f)
+	{
+		m_onWrite.push_back(std::forward<Func>(f));
+	}
+
+	void poll(int timeout)
+	{
+		auto st = m_listener.wait(timeout);
+
+		if (st.socket == m_master.handle()) {
+			processAccept();
+		} else {
+			processSync(st);
+		}
+	}
+
+	inline int wait()
+	{
+		poll(-1);
+	}
+};
+
+#endif
+
 } // !net
 
 #endif // !_SOCKETS_H_