Mercurial > code
view C++/SocketListener.h @ 319:cba77da58496
* Finalize SocketSsl
* Add documentation
author | David Demelier <markand@malikania.fr> |
---|---|
date | Sun, 08 Mar 2015 11:04:01 +0100 |
parents | 4c0af1143fc4 |
children |
line wrap: on
line source
/* * SocketListener.h -- portable select() wrapper * * 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_LISTENER_NG_H_ #define _SOCKET_LISTENER_NG_H_ #include <chrono> #include <functional> #include <initializer_list> #include <map> #include <memory> #include <utility> #include <vector> #include "Socket.h" #if defined(_WIN32) # if _WIN32_WINNT >= 0x0600 # define SOCKET_HAVE_POLL # endif #else # define SOCKET_HAVE_POLL #endif /** * @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. */ class SocketStatus { public: Socket &socket; //!< which socket is ready int direction; //!< the direction }; /** * @class SocketListenerInterface * @brief Implement the polling method */ class SocketListenerInterface { public: /** * Default destructor. */ virtual ~SocketListenerInterface() = default; /** * Add a socket with a specified direction. * * @param s the socket * @param direction the direction */ virtual void set(Socket &sc, int direction) = 0; /** * Remove a socket with a specified direction. * * @param s the socket * @param direction the direction */ virtual void unset(Socket &sc, int direction) = 0; /** * Remove completely a socket. * * @param sc the socket to remove */ virtual void remove(Socket &sc) = 0; /** * Remove all sockets. */ virtual void clear() = 0; /** * Select one 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; /** * Select many sockets. * * @param ms the number of milliseconds to wait, -1 means forever * @return a vector of ready sockets * @throw error::Failure on failure * @throw error::Timeout on timeout */ virtual std::vector<SocketStatus> selectMultiple(int ms) = 0; }; /** * @class SocketListener * @brief Synchronous multiplexing * * Convenient wrapper around the select() system call. * * This class is implemented using a bridge pattern to allow different uses * of listener implementation. * * Currently, poll and select() are available. * * This wrappers takes abstract sockets as non-const reference but it does not * own them so you must take care that sockets are still alive until the * SocketListener is destroyed. */ class SocketListener final { public: #if defined(SOCKET_HAVE_POLL) static constexpr const SocketMethod PreferredMethod = SocketMethod::Poll; #else static constexpr const SocketMethod PreferredMethod = SocketMethod::Select; #endif static const int Read; static const int Write; using Map = std::map<std::reference_wrapper<Socket>, int>; using Iface = std::unique_ptr<SocketListenerInterface>; private: Map m_map; Iface m_interface; public: /** * Move constructor. * * @param other the other object */ SocketListener(SocketListener &&other) = default; /** * Move operator. * * @param other the other object * @return this */ SocketListener &operator=(SocketListener &&other) = default; /** * Create a socket listener. * * @param method the preferred method */ SocketListener(SocketMethod method = PreferredMethod); /** * Create a listener from a list of sockets. * * @param list the list */ SocketListener(std::initializer_list<std::pair<std::reference_wrapper<Socket>, int>> list); /** * Return an iterator to the beginning. * * @return the iterator */ inline auto begin() noexcept { return m_map.begin(); } /** * Overloaded function. * * @return the iterator */ inline auto begin() const noexcept { return m_map.begin(); } /** * Overloaded function. * * @return the iterator */ inline auto cbegin() const noexcept { return m_map.cbegin(); } /** * Return an iterator to the end. * * @return the iterator */ inline auto end() noexcept { return m_map.end(); } /** * Overloaded function. * * @return the iterator */ inline auto end() const noexcept { return m_map.end(); } /** * Overloaded function. * * @return the iterator */ inline auto cend() const noexcept { return m_map.cend(); } /** * Add a socket to the listener. * * @param sc the socket * @param direction (may be OR'ed) */ void set(Socket &sc, int direction); /** * Unset a socket from the listener, only the direction is removed * unless the two directions are requested. * * For example, if you added a socket for both reading and writing, * unsetting the write direction will keep the socket for reading. * * @param sc the socket * @param direction the direction (may be OR'ed) * @see remove */ void unset(Socket &sc, int direction) noexcept; /** * Remove completely the socket from the listener. * * @param sc the socket */ inline void remove(Socket &sc) noexcept { m_map.erase(sc); m_interface->remove(sc); } /** * Remove all sockets. */ inline void clear() noexcept { m_map.clear(); m_interface->clear(); } /** * Get the number of sockets in the listener. */ unsigned size() const noexcept { return m_map.size(); } /** * Select a socket. Waits for a specific amount of time specified as the duration. * * @param duration the duration * @return the socket ready */ 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 with milliseconds. * * @param timeout the optional timeout in milliseconds * @return the socket ready */ inline SocketStatus select(int timeout = -1) { return m_interface->select(timeout); } /** * Select multiple sockets. * * @param duration the duration * @return the socket ready */ template <typename Rep, typename Ratio> inline std::vector<SocketStatus> selectMultiple(const std::chrono::duration<Rep, Ratio> &duration) { auto cvt = std::chrono::duration_cast<std::chrono::milliseconds>(duration); return m_interface->selectMultiple(cvt.count()); } /** * Overload with milliseconds. * * @return the socket ready */ inline std::vector<SocketStatus> selectMultiple(int timeout = -1) { return m_interface->selectMultiple(timeout); } }; #endif // !_SOCKET_LISTENER_NG_H_