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_