Mercurial > code
changeset 170:fd138f2a9773
Add C++ portable sockets
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 10 Sep 2013 15:17:56 +0200 |
parents | 29531c2f8213 |
children | e47c4e9e3f9d |
files | C++/Socket.cpp C++/Socket.h C++/SocketAddress.cpp C++/SocketAddress.h C++/SocketListener.cpp C++/SocketListener.h |
diffstat | 6 files changed, 928 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/Socket.cpp Tue Sep 10 15:17:56 2013 +0200 @@ -0,0 +1,266 @@ +/* + * Socket.cpp -- portable C++ socket wrappers + * + * Copyright (c) 2013, 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. + */ + +#include <cerrno> + +#include "Socket.h" +#include "SocketAddress.h" + +/* -------------------------------------------------------- + * SocketError implementation + * -------------------------------------------------------- */ + +SocketError::SocketError(const std::string &error) +{ + m_error = error; +} + +const char *SocketError::what() const throw() +{ + return m_error.c_str(); +} + +/* -------------------------------------------------------- + * Socket implementation + * -------------------------------------------------------- */ + +void Socket::init() +{ +#if defined(_WIN32) + WSADATA wsa; + WSAStartup(MAKEWORD(2, 2), &wsa); +#endif +} + +/* -------------------------------------------------------- + * System dependent code + * -------------------------------------------------------- */ + +#if defined(_WIN32) + +string Socket::getLastSysError() +{ + LPSTR str = nullptr; + string errmsg = "Unknown error"; + + FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + WSAGetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&str, 0, NULL); + + + if (str) + { + errmsg = string(str); + LocalFree(str); + } + + return errmsg; +} + +#else + +std::string Socket::getLastSysError() +{ + return strerror(errno); +} + +#endif + +void Socket::finish() +{ +#if defined(_WIN32) + WSACleanup(); +#endif +} + +Socket::Socket() +{ +} + +Socket::Socket(int domain, int type, int protocol) +{ + m_socket = socket(domain, type, protocol); + + if (m_socket == INVALID_SOCKET) + throw SocketError(getLastSysError()); +} + +Socket::Socket(Socket::Type sock) + : m_socket(sock) +{ +} + +Socket::~Socket() +{ +} + +Socket::Type Socket::getSocket() const +{ + return m_socket; +} + +void Socket::set(int level, int name, const void *arg, unsigned argLen) +{ + if (setsockopt(m_socket, level, name, (Socket::ConstArg)arg, argLen) == SOCKET_ERROR) + throw SocketError(getLastSysError()); +} + +void Socket::blockMode(bool block) +{ +#if defined(O_NONBLOCK) && !defined(_WIN32) + int flags; + + if ((flags = fcntl(m_socket, F_GETFL, 0)) == -1) + flags = 0; + + if (!block) + flags &= ~(O_NONBLOCK); + else + flags |= O_NONBLOCK; + + if (fcntl(m_socket, F_SETFL, flags) == -1) + throw SocketError(getLastSysError()); +#else + unsigned long flags = (block) ? 0 : 1; + + if (ioctlsocket(m_socket, FIONBIO, &flags) == SOCKET_ERROR) + throw SocketError(getLastSysError()); +#endif +} + +void Socket::bind(const SocketAddress &addr) +{ + const sockaddr_storage &sa = addr.address(); + size_t addrlen = addr.length(); + + if (::bind(m_socket, (sockaddr *)&sa, addrlen) == SOCKET_ERROR) + throw SocketError(getLastSysError()); +} + +void Socket::connect(const SocketAddress &addr) +{ + const sockaddr_storage &sa = addr.address(); + size_t addrlen = addr.length(); + + if (::connect(m_socket, (sockaddr *)&sa, addrlen) == SOCKET_ERROR) + throw SocketError(getLastSysError()); +} + +Socket Socket::accept() +{ + SocketAddress dummy; + + return accept(dummy); +} + +Socket Socket::accept(SocketAddress &info) +{ + Socket::Type sock; + + info.m_addrlen = sizeof (sockaddr_storage); + sock = ::accept(m_socket, (sockaddr *)&info.m_addr, &info.m_addrlen); + + if (sock == INVALID_SOCKET) + throw SocketError(getLastSysError()); + + return Socket(sock); +} + +void Socket::listen(int max) +{ + if (::listen(m_socket, max) == SOCKET_ERROR) + throw SocketError(getLastSysError()); +} + +unsigned Socket::recv(void *data, unsigned dataLen) +{ + int nbread; + + nbread = ::recv(m_socket, (Socket::Arg)data, dataLen, 0); + if (nbread == SOCKET_ERROR) + throw SocketError(getLastSysError()); + + return (unsigned)nbread; +} + +unsigned Socket::send(const void *data, unsigned dataLen) +{ + int nbsent; + + nbsent = ::send(m_socket, (Socket::ConstArg)data, dataLen, 0); + if (nbsent == SOCKET_ERROR) + throw SocketError(getLastSysError()); + + return (unsigned)nbsent; +} + +unsigned Socket::send(const std::string &message) +{ + return Socket::send(message.c_str(), message.length()); +} + +unsigned Socket::recvfrom(void *data, unsigned dataLen) +{ + SocketAddress dummy; + + return recvfrom(data, dataLen, dummy); +} + +unsigned Socket::recvfrom(void *data, unsigned dataLen, SocketAddress &info) +{ + int nbread; + + info.m_addrlen = sizeof (struct sockaddr_storage); + nbread = ::recvfrom(m_socket, (Socket::Arg)data, dataLen, 0, + (sockaddr *)&info.m_addr, &info.m_addrlen); + + if (nbread == SOCKET_ERROR) + throw SocketError(getLastSysError()); + + return (unsigned)nbread; +} + +unsigned Socket::sendto(const void *data, unsigned dataLen, const SocketAddress &info) +{ + int nbsent; + + nbsent = ::sendto(m_socket, (Socket::ConstArg)data, dataLen, 0, + (const sockaddr *)&info.m_addr, info.m_addrlen); + if (nbsent == SOCKET_ERROR) + throw SocketError(getLastSysError()); + + return (unsigned)nbsent; +} + +unsigned Socket::sendto(const std::string &message, const SocketAddress &info) +{ + return sendto(message.c_str(), message.length(), info); +} + +void Socket::close() +{ + (void)closesocket(m_socket); +} + +bool operator==(const Socket &s1, const Socket &s2) +{ + return s1.getSocket() == s2.getSocket(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/Socket.h Tue Sep 10 15:17:56 2013 +0200 @@ -0,0 +1,285 @@ +/* + * Socket.h -- portable C++ socket wrappers + * + * Copyright (c) 2013, 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_H_ +#define _SOCKET_H_ + +#include <exception> +#include <memory> +#include <string> + +#if defined(_WIN32) +# include <WinSock2.h> +# include <WS2tcpip.h> +#else +# 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> + +# define ioctlsocket(s, p, a) ::ioctl(s, p, a) +# define closesocket(s) ::close(s) + +# define gai_strerrorA gai_strerror + +# define INVALID_SOCKET -1 +# define SOCKET_ERROR -1 +#endif + +class SocketAddress; + +/** + * @class SocketError + * @brief socket error reporting + * + * This class is mainly used in all socket operations that may fail. + */ +class SocketError : public std::exception +{ +private: + std::string m_error; + +public: + SocketError(const std::string &error); + + virtual const char * what(void) const throw(); +}; + +/** + * @class Socket + * @brief socket abstraction + * + * This class is a big wrapper around sockets functions but portable, + * there is some functions that helps for getting error reporting. + */ +class Socket +{ +public: +#if defined(_WIN32) + typedef SOCKET Type; + typedef const char * ConstArg; + typedef char * Arg; +#else + typedef int Type; + typedef const void * ConstArg; + typedef void * Arg; +#endif + +protected: + Socket::Type m_socket; + +public: + /** + * To be called before any socket operation. + */ + static void init(); + + /** + * Get the last socket system error. + * + * @return a string message + */ + static std::string getLastSysError(); + + /** + * To be called before exiting. + */ + static void finish(); + + /** + * Default constructor. + */ + Socket(); + + /** + * Constructor to create a new socket. + * + * @param domain the domain + * @param type the type + * @param protocol the optional protocol + * @throw SocketError on error + */ + Socket(int domain, int type, int protocol = 0); + + /** + * Constructor with a socket already opened. + * + * @param sock the socket + */ + Socket(Socket::Type sock); + + /** + * Default destructor. + */ + virtual ~Socket(); + + /** + * Get the socket. + * + * @return the socket + */ + Type getSocket() const; + + /** + * Set an option for the socket. + * + * @param level the setting level + * @param name the name + * @param arg the value + * @param argLen the argument length + * @throw SocketError on error + */ + void set(int level, int name, const void *arg, unsigned argLen); + + /** + * Enable or disable blocking mode. + * + * @param block the mode + */ + void blockMode(bool block = true); + + /** + * Bind the socket. + * + * @param location a IP or Unix location + * @throw SocketError error + */ + void bind(const SocketAddress &address); + + /** + * Try to connect to the specific address + * + * @param addr the address + * @throw SocketError on error + */ + void connect(const SocketAddress &address); + + /** + * Accept a client without getting its info. + * + * @return a client ready to use + * @throw SocketError on error + */ + Socket accept(); + + /** + * Accept a client. + * + * @param info the optional client info + * @return a client ready to use + * @throw SocketError on error + */ + Socket accept(SocketAddress &info); + + /** + * Listen to a specific number of pending connections. + * + * @param max the max number of clients + * @throw SocketError on error + */ + void listen(int max); + + /** + * Receive some data. + * + * @param data the destination pointer + * @param dataLen max length to receive + * @return the number of bytes received + * @throw SocketError on error + */ + unsigned recv(void *data, unsigned dataLen); + + /** + * Send some data. + * + * @param data the data to send + * @param dataLen the data length + * @return the number of bytes sent + * @throw SocketError on error + */ + unsigned send(const void *data, unsigned dataLen); + + /** + * Send a message as a string. + * + * @param message the message + * @return the number of bytes sent + * @throw SocketError on error + */ + unsigned send(const std::string &message); + + /** + * Receive from a connection-less socket without getting + * client information. + * + * @param data the destination pointer + * @param dataLen max length to receive + * @return the number of bytes received + * @throw SocketError on error + */ + unsigned recvfrom(void *data, unsigned dataLen); + + /** + * Receive from a connection-less socket and get the client + * information. + * + * @param data the destination pointer + * @param dataLen max length to receive + * @param info the client info + * @return the number of bytes received + * @throw SocketError on error + */ + unsigned recvfrom(void *data, unsigned dataLen, SocketAddress &info); + + /** + * Send some data to a connection-less socket. + * + * @param data the data to send + * @param dataLen the data length + * @param address the address + * @return the number of bytes sent + * @throw SocketError on error + */ + unsigned sendto(const void *data, unsigned dataLen, const SocketAddress &info); + + /** + * Send a message to a connection-less socket. + * + * @param message the message + * @param address the address + * @return the number of bytes sent + * @throw SocketError on error + */ + unsigned sendto(const std::string &message, const SocketAddress &info); + + /** + * Close the socket. + */ + void close(); +}; + +bool operator==(const Socket &s1, const Socket &s2); + +#endif // !_SOCKET_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/SocketAddress.cpp Tue Sep 10 15:17:56 2013 +0200 @@ -0,0 +1,111 @@ +/* + * SocketAddress.cpp -- socket addresses management + * + * Copyright (c) 2013, 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. + */ + +#include "SocketAddress.h" + +/* -------------------------------------------------------- + * BindAddressIP implementation + * -------------------------------------------------------- */ + +BindAddressIP::BindAddressIP(const std::string &iface, unsigned port, int family) + : m_host(iface) + , m_family(family) + , m_port(port) +{ + if (m_family == AF_INET6) + { + sockaddr_in6 *ptr = (sockaddr_in6 *)&m_addr; + + ptr->sin6_family = AF_INET6; + ptr->sin6_port = htons(m_port); + + if (m_host == "*") + ptr->sin6_addr = in6addr_any; + else if (inet_pton(AF_INET6, m_host.c_str(), &ptr->sin6_addr) <= 0) + throw SocketError(Socket::getLastSysError()); + + m_addrlen = sizeof (sockaddr_in6); + } + else + { + sockaddr_in *ptr = (sockaddr_in *)&m_addr; + + ptr->sin_family = AF_INET; + ptr->sin_port = htons(m_port); + + if (m_host == "*") + ptr->sin_addr.s_addr = INADDR_ANY; + else if (inet_pton(AF_INET, m_host.c_str(), &ptr->sin_addr) <= 0) + throw SocketError(Socket::getLastSysError()); + + m_addrlen = sizeof (sockaddr_in); + } +} + +/* -------------------------------------------------------- + * ConnectAddressIP implementation + * -------------------------------------------------------- */ + +ConnectAddressIP::ConnectAddressIP(const std::string &host, unsigned port, int family, int type) +{ + addrinfo hints, *res; + int error; + + memset(&hints, 0, sizeof (hints)); + hints.ai_family = family; + hints.ai_socktype = type; + + error = getaddrinfo(host.c_str(), std::to_string(port).c_str(), &hints, &res); + if (error) + throw SocketError(gai_strerrorA(error)); + + memcpy(&m_addr, res->ai_addr, res->ai_addrlen); + m_addrlen = res->ai_addrlen; + + freeaddrinfo(res); +} + +/* -------------------------------------------------------- + * SocketAddress implementation + * -------------------------------------------------------- */ + +SocketAddress::SocketAddress() + : m_addrlen(0) +{ + memset(&m_addr, 0, sizeof (m_addr)); +} + +SocketAddress::SocketAddress(const sockaddr_storage &addr, socklen_t length) + : m_addr(addr) + , m_addrlen(length) +{ +} + +SocketAddress::~SocketAddress() +{ +} + +const sockaddr_storage &SocketAddress::address() const +{ + return m_addr; +} + +socklen_t SocketAddress::length() const +{ + return m_addrlen; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/SocketAddress.h Tue Sep 10 15:17:56 2013 +0200 @@ -0,0 +1,115 @@ +/* + * SocketAddress.h -- socket addresses management + * + * Copyright (c) 2013, 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_ADDRESS_H_ +#define _SOCKET_ADDRESS_H_ + +#include "Socket.h" + +class SocketAddress +{ +protected: + sockaddr_storage m_addr; + socklen_t m_addrlen; + +public: + // Friends. + friend class Socket; + + /** + * Default constructor. + */ + SocketAddress(); + + /** + * Constructor with address and size. + * + * @param addr the address + * @param length the address length + */ + SocketAddress(const sockaddr_storage &addr, socklen_t length); + + /** + * Default destructor. + */ + virtual ~SocketAddress(); + + /** + * Get the address length + * + * @return the length + */ + socklen_t length() const; + + /** + * Get the address. + * + * @return the address + */ + const sockaddr_storage &address() const; +}; + +/** + * @class BindAddressIP + * @brief internet protocol bind class + * + * Create a bind address for internet protocol, + * IPv4 or IPv6. + */ +class BindAddressIP : public SocketAddress +{ +private: + std::string m_host; + int m_family; + unsigned m_port; + +public: + /** + * Create a bind end point. + * + * @param addr the interface to bind + * @param port the port + * @param family AF_INET or AF_INET6 + * @throw SocketError on error + */ + BindAddressIP(const std::string &addr, unsigned port, int family); +}; + +/** + * @class ConnectAddressIP + * @brief internet protocol connect class + * + * Create a connect address for internet protocol, + * using getaddrinfo(3). + */ +class ConnectAddressIP : public SocketAddress +{ +public: + /** + * Create a connect end point. + * + * @param host the hostname + * @param port the port + * @param family AF_INET, AF_INET6, ... + * @param type of socket SOCK_STREAM, SOCK_DGRAM, ... + * @throw SocketError on error + */ + ConnectAddressIP(const std::string &host, unsigned port, int family, int type = SOCK_STREAM); +}; + +#endif // !_SOCKET_ADDRESS_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/SocketListener.cpp Tue Sep 10 15:17:56 2013 +0200 @@ -0,0 +1,75 @@ +/* + * SocketListener.cpp -- portable select() wrapper + * + * Copyright (c) 2013, 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. + */ + +#include <algorithm> + +#include "SocketListener.h" + +const char *SocketTimeout::what() const throw() +{ + return "Timeout occured"; +} + +void SocketListener::add(Socket &s) +{ + m_clients.push_back(s); +} + +void SocketListener::remove(Socket &s) +{ + m_clients.erase(std::remove(m_clients.begin(), m_clients.end(), s), m_clients.end()); +} + +void SocketListener::clear(void) +{ + m_clients.clear(); +} + +Socket & SocketListener::select(int s, int us) +{ + fd_set fds; + timeval maxwait, *towait; + int error; + int fdmax = m_clients.front().getSocket(); + + FD_ZERO(&fds); + for (Socket &c : m_clients) + { + FD_SET(c.getSocket(), &fds); + if ((int)c.getSocket() > fdmax) + fdmax = c.getSocket(); + } + + maxwait.tv_sec = s; + maxwait.tv_usec = us; + + // Set to NULL for infinite timeout. + towait = (s == 0 && us == 0) ? nullptr : &maxwait; + + error = ::select(fdmax + 1, &fds, NULL, NULL, towait); + if (error == SOCKET_ERROR) + throw SocketError(Socket::getLastSysError()); + if (error == 0) + throw SocketTimeout(); + + for (Socket &c : m_clients) + if (FD_ISSET(c.getSocket(), &fds)) + return c; + + throw SocketError("No socket found"); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/SocketListener.h Tue Sep 10 15:17:56 2013 +0200 @@ -0,0 +1,76 @@ +/* + * SocketListener.h -- portable select() wrapper + * + * Copyright (c) 2013, 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_H_ +#define _SOCKET_LISTENER_H_ + +#include <vector> + +#include "Socket.h" + +/** + * @class SocketTimeout + * @brief thrown when a timeout occured + */ +class SocketTimeout : public std::exception +{ +public: + virtual const char * what(void) const throw(); +}; + +class SocketListener +{ +public: + +private: + std::vector<Socket> m_clients; + +public: + /** + * Add a socket to listen to. + * + * @param s the socket + */ + void add(Socket &s); + + /** + * Remove a socket from the list. + * + * @param s the socket + */ + void remove(Socket &s); + + /** + * Remove every sockets in the listener. + */ + void clear(); + + /** + * Wait for an event in the socket list. If both s and us are set to 0 then + * it waits indefinitely. + * + * @param s the timeout in seconds + * @param us the timeout in milliseconds + * @return the socket ready + * @throw SocketError on error + * @throw SocketTimeout on timeout + */ + Socket &select(int s = 0, int us = 0); +}; + +#endif // !_SOCKET_LISTENER_H_