Mercurial > code
changeset 380:06b0f405c58f
Socket: initial support for object-oriented addresses
author | David Demelier <markand@malikania.fr> |
---|---|
date | Fri, 19 Jun 2015 13:56:12 +0200 |
parents | 57ce1a6293b9 |
children | 9fd636045546 |
files | C++/modules/Socket/Socket.cpp C++/modules/Socket/Socket.h C++/modules/Socket/SocketAddress.cpp C++/modules/Socket/SocketAddress.h C++/modules/Socket/SocketTcp.cpp C++/modules/Socket/SocketUdp.cpp C++/modules/Socket/SocketUdp.h C++/tests/Socket/main.cpp |
diffstat | 8 files changed, 226 insertions(+), 131 deletions(-) [+] |
line wrap: on
line diff
--- a/C++/modules/Socket/Socket.cpp Fri Jun 19 11:18:53 2015 +0200 +++ b/C++/modules/Socket/Socket.cpp Fri Jun 19 13:56:12 2015 +0200 @@ -142,26 +142,21 @@ close(); } -SocketAddress Socket::address() const +std::unique_ptr<SocketAddress> Socket::address() const { -#if defined(_WIN32) - int length; -#else socklen_t length; -#endif - sockaddr_storage ss; if (getsockname(m_handle, (sockaddr *)&ss, &length) == Error) throw SocketError(SocketError::System, "getsockname"); - return SocketAddress(ss, length); + return SocketAddress::decode(ss, length); } -void Socket::bind(const SocketAddress &address) +void Socket::bind(const std::unique_ptr<SocketAddress> &address) { - const auto &sa = address.address(); - const auto addrlen = address.length(); + const auto &sa = address->address(); + const auto addrlen = address->length(); if (::bind(m_handle, reinterpret_cast<const sockaddr *>(&sa), addrlen) == Error) { throw SocketError(SocketError::System, "bind");
--- a/C++/modules/Socket/Socket.h Fri Jun 19 11:18:53 2015 +0200 +++ b/C++/modules/Socket/Socket.h Fri Jun 19 13:56:12 2015 +0200 @@ -38,6 +38,7 @@ #include <cstdlib> #include <cstring> #include <exception> +#include <memory> #include <string> #if defined(_WIN32) @@ -327,7 +328,7 @@ * * @param handle the native descriptor */ - inline Socket(Handle handle) + inline Socket(Handle handle) noexcept : m_handle{handle} , m_state{SocketState::Opened} { @@ -366,7 +367,7 @@ * @return the address * @throw SocketError on failures */ - SocketAddress address() const; + std::unique_ptr<SocketAddress> address() const; /** * Set an option for the socket. @@ -439,7 +440,7 @@ * @param address the address * @throw SocketError on any error */ - void bind(const SocketAddress &address); + void bind(const std::unique_ptr<SocketAddress> &address); /** * Set the blocking mode, if set to false, the socket will be marked
--- a/C++/modules/Socket/SocketAddress.cpp Fri Jun 19 11:18:53 2015 +0200 +++ b/C++/modules/Socket/SocketAddress.cpp Fri Jun 19 13:56:12 2015 +0200 @@ -22,6 +22,23 @@ #include "Socket.h" #include "SocketAddress.h" +std::unique_ptr<SocketAddress> SocketAddress::decode(const struct sockaddr_storage &ss, socklen_t length) +{ + switch (ss.ss_family) { + case AF_INET: + case AF_INET6: + return std::make_unique<address::Internet>(ss, length); +#if !defined(_WIN32) + case AF_UNIX: + return std::make_unique<address::Unix>(ss, length); +#endif + default: + break; + } + + throw std::invalid_argument{"not a valid address type"}; +} + namespace address { /* -------------------------------------------------------- @@ -29,24 +46,21 @@ * -------------------------------------------------------- */ Internet::Internet(const std::string &host, unsigned port, int domain) + : m_domain{domain} { if (host == "*") { - if (domain == AF_INET6) { - sockaddr_in6 *ptr = (sockaddr_in6 *)&m_addr; - - ptr->sin6_addr = in6addr_any; - ptr->sin6_family = AF_INET6; - ptr->sin6_port = htons(port); + if (m_domain == AF_INET6) { + std::memset(&m_sin6, 0, sizeof (struct sockaddr_in6)); - m_addrlen = sizeof (sockaddr_in6); + m_sin6.sin6_addr = in6addr_any; + m_sin6.sin6_family = AF_INET6; + m_sin6.sin6_port = htons(port); } else { - sockaddr_in *ptr = (sockaddr_in *)&m_addr; + std::memset(&m_sin, 0, sizeof (struct sockaddr_in)); - ptr->sin_addr.s_addr = INADDR_ANY; - ptr->sin_family = AF_INET; - ptr->sin_port = htons(port); - - m_addrlen = sizeof (sockaddr_in); + m_sin.sin_addr.s_addr = INADDR_ANY; + m_sin.sin_family = AF_INET; + m_sin.sin_port = htons(port); } } else { addrinfo hints, *res; @@ -59,80 +73,100 @@ throw SocketError(SocketError::System, "getaddrinfo", gai_strerror(error)); } - std::memcpy(&m_addr, res->ai_addr, res->ai_addrlen); - m_addrlen = res->ai_addrlen; + if (m_domain == AF_INET6) { + std::memcpy(&m_sin6, res->ai_addr, sizeof (struct sockaddr_in6)); + } else { + std::memcpy(&m_sin, res->ai_addr, sizeof (struct sockaddr_in)); + } + freeaddrinfo(res); } } +Internet::Internet(const sockaddr_storage &ss, socklen_t length) + : m_domain{ss.ss_family} +{ + if (ss.ss_family == AF_INET6) { + std::memcpy(&m_sin6, &ss, length); + } else { + std::memcpy(&m_sin, &ss, length); + } +} + +socklen_t Internet::length() const noexcept +{ + return (m_domain == AF_INET6) ? sizeof (struct sockaddr_in6) : sizeof (struct sockaddr_in); +} + +const sockaddr &Internet::address() const noexcept +{ + // Can't get a ternary operator to work here. + if (m_domain == AF_INET6) + return reinterpret_cast<const sockaddr &>(m_sin6); + + return reinterpret_cast<const sockaddr &>(m_sin); +} + +SocketAddressInfo Internet::info() const +{ + std::string type = (m_domain == AF_INET6) ? "ipv6" : "ipv4"; + std::string port = std::to_string(m_domain == AF_INET6 ? ntohs(m_sin6.sin6_port) : ntohs(m_sin.sin_port)); + + return SocketAddressInfo{ + { "type", type }, + { "port", port } + }; +} + /* -------------------------------------------------------- * Unix implementation * -------------------------------------------------------- */ #if !defined(_WIN32) -#include <sys/un.h> - -Unix::Unix(const std::string &path, bool rm) +Unix::Unix(std::string path, bool rm) + : m_path{std::move(path)} { - sockaddr_un *sun = (sockaddr_un *)&m_addr; - // Silently remove the file even if it fails if (rm) { - ::remove(path.c_str()); + ::remove(m_path.c_str()); } // Copy the path - memset(sun->sun_path, 0, sizeof (sun->sun_path)); - strncpy(sun->sun_path, path.c_str(), sizeof (sun->sun_path) - 1); + memset(m_sun.sun_path, 0, sizeof (m_sun.sun_path)); + strncpy(m_sun.sun_path, path.c_str(), sizeof (m_sun.sun_path) - 1); // Set the parameters - sun->sun_family = AF_UNIX; - m_addrlen = SUN_LEN(sun); + m_sun.sun_family = AF_UNIX; +} + +Unix::Unix(const sockaddr_storage &ss, socklen_t) +{ + m_path = reinterpret_cast<const struct sockaddr_un &>(ss).sun_path; +} + +socklen_t Unix::length() const noexcept +{ +#if defined(SOCKET_HAVE_SUN_LEN) + return SUN_LEN(m_sun); +#else + return sizeof (m_sun); +#endif +} + +const sockaddr &Unix::address() const noexcept +{ + return reinterpret_cast<const sockaddr &>(m_sun); +} + +SocketAddressInfo Unix::info() const +{ + return SocketAddressInfo{ + { "type", "unix" }, + { "path", m_path } + }; } #endif // _WIN32 } // !address - -/* -------------------------------------------------------- - * 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) -{ -} - -const sockaddr_storage &SocketAddress::address() const -{ - return m_addr; -} - -socklen_t SocketAddress::length() const -{ - return m_addrlen; -} - -bool operator<(const SocketAddress &s1, const SocketAddress &s2) -{ - const auto &array1 = reinterpret_cast<const unsigned char *>(&s1.address()); - const auto &array2 = reinterpret_cast<const unsigned char *>(&s2.address()); - - return std::lexicographical_compare(array1, array1 + s1.length(), array2, array2 + s2.length()); -} - -bool operator==(const SocketAddress &s1, const SocketAddress &s2) -{ - const auto &array1 = reinterpret_cast<const unsigned char *>(&s1.address()); - const auto &array2 = reinterpret_cast<const unsigned char *>(&s2.address()); - - return std::equal(array1, array1 + s1.length(), array2, array2 + s2.length()); -}
--- a/C++/modules/Socket/SocketAddress.h Fri Jun 19 11:18:53 2015 +0200 +++ b/C++/modules/Socket/SocketAddress.h Fri Jun 19 13:56:12 2015 +0200 @@ -19,15 +19,37 @@ #ifndef _SOCKET_ADDRESS_NG_H_ #define _SOCKET_ADDRESS_NG_H_ +/** + * @file SocketAddress.h + * @brief Describe addresses + * + * User may set the following variables before compiling these files: + * + * SOCKET_HAVE_SUN_LEN - (bool) Some systems do not have SUN_LEN which is the preferred way to + * compute the address size for a Unix address. Otherwise, sizeof is used. + */ + + +#include <memory> #include <string> +#include <unordered_map> #if defined(_WIN32) # include <Winsock2.h> # include <Ws2tcpip.h> #else # include <sys/socket.h> +# include <sys/un.h> +# include <arpa/inet.h> +# include <netinet/in.h> #endif + +/** + * Generic information table for an address. + */ +using SocketAddressInfo = std::unordered_map<std::string, std::string>; + /** * @class SocketAddress * @brief base class for socket addresses @@ -39,23 +61,21 @@ * @see Unix */ class SocketAddress { -protected: - sockaddr_storage m_addr; - socklen_t m_addrlen; +public: + /** + * Decode an address into the appropriate object. Useful for recvfrom, accept. + * + * @param ss the storage address + * @param length the address length + * @return the real object + * @throw std::invalid_argument on error + */ + static std::unique_ptr<SocketAddress> decode(const struct sockaddr_storage &ss, socklen_t length); -public: /** * 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); + SocketAddress() = default; /** * Default destructor. @@ -67,32 +87,21 @@ * * @return the length */ - socklen_t length() const; + virtual socklen_t length() const noexcept = 0; /** * Get the address. * * @return the address */ - const sockaddr_storage &address() const; + virtual const sockaddr &address() const noexcept = 0; /** - * Compare the addresses. The check is lexicographical. + * Get the information about the address. * - * @param s1 the first address - * @param s2 the second address - * @return true if s1 is less than s2 + * @return the information */ - friend bool operator<(const SocketAddress &s1, const SocketAddress &s2); - - /** - * Compare the addresses. - * - * @param s1 the first address - * @param s2 the second address - * @return true if s1 == s2 - */ - friend bool operator==(const SocketAddress &s1, const SocketAddress &s2); + virtual SocketAddressInfo info() const = 0; }; namespace address { @@ -105,16 +114,47 @@ * using getaddrinfo(3). */ class Internet : public SocketAddress { +private: + union { + struct sockaddr_in m_sin; + struct sockaddr_in6 m_sin6; + }; + + int m_domain{AF_INET}; + public: /** * Create an IPv4 or IPV6 end point. * * @param host the hostname * @param port the port - * @param family AF_INET, AF_INET6, ... + * @param domain AF_INET or AF_INET6 * @throw SocketError on error */ - Internet(const std::string &host, unsigned port, int family); + Internet(const std::string &host, unsigned port, int domain); + + /** + * Construct an internet address from a storage address. + * + * @param ss the storage + * @param length the length + */ + Internet(const struct sockaddr_storage &ss, socklen_t length); + + /** + * @copydoc SocketAddress::length + */ + socklen_t length() const noexcept override; + + /** + * @copydoc SocketAddress::address + */ + const sockaddr &address() const noexcept override; + + /** + * @copydoc SocketAddress::info + */ + SocketAddressInfo info() const override; }; #if !defined(_WIN32) @@ -126,6 +166,10 @@ * Create an address to a specific path. Only available on Unix. */ class Unix : public SocketAddress { +private: + struct sockaddr_un m_sun; + std::string m_path; + public: /** * Construct an address to a path. @@ -133,7 +177,30 @@ * @param path the path * @param rm remove the file before (default: false) */ - Unix(const std::string &path, bool rm = false); + Unix(std::string path, bool rm = false); + + /** + * Construct an unix address from a storage address. + * + * @param ss the storage + * @param length the length + */ + Unix(const struct sockaddr_storage &ss, socklen_t length); + + /** + * @copydoc SocketAddress::length + */ + socklen_t length() const noexcept override; + + /** + * @copydoc SocketAddress::address + */ + const sockaddr &address() const noexcept override; + + /** + * @copydoc SocketAddress::info + */ + SocketAddressInfo info() const override; }; #endif // ! !_WIN32
--- a/C++/modules/Socket/SocketTcp.cpp Fri Jun 19 11:18:53 2015 +0200 +++ b/C++/modules/Socket/SocketTcp.cpp Fri Jun 19 13:56:12 2015 +0200 @@ -62,8 +62,7 @@ #endif } - // TODO: add it - //info = SocketAddress(address, addrlen); + info = SocketAddress::decode(address, addrlen); return std::make_unique<SocketTcp>(handle); }
--- a/C++/modules/Socket/SocketUdp.cpp Fri Jun 19 11:18:53 2015 +0200 +++ b/C++/modules/Socket/SocketUdp.cpp Fri Jun 19 13:56:12 2015 +0200 @@ -24,7 +24,7 @@ { } -unsigned SocketUdp::recvfrom(void *data, unsigned length, SocketAddress &info) +unsigned SocketUdp::recvfrom(void *data, unsigned length, std::unique_ptr<SocketAddress> &info) { int nbread; @@ -35,7 +35,7 @@ addrlen = sizeof (struct sockaddr_storage); nbread = ::recvfrom(m_handle, (Socket::Arg)data, length, 0, (sockaddr *)&address, &addrlen); - info = SocketAddress(address, addrlen); + info = SocketAddress::decode(address, addrlen); if (nbread == Error) { #if defined(_WIN32) @@ -58,11 +58,11 @@ return (unsigned)nbread; } -unsigned SocketUdp::sendto(const void *data, unsigned length, const SocketAddress &info) +unsigned SocketUdp::sendto(const void *data, unsigned length, const std::unique_ptr<SocketAddress> &info) { int nbsent; - nbsent = ::sendto(m_handle, (Socket::ConstArg)data, length, 0, (const sockaddr *)&info.address(), info.length()); + nbsent = ::sendto(m_handle, (Socket::ConstArg)data, length, 0, (const sockaddr *)&info->address(), info->length()); if (nbsent == Error) { #if defined(_WIN32) int error = WSAGetLastError();
--- a/C++/modules/Socket/SocketUdp.h Fri Jun 19 11:18:53 2015 +0200 +++ b/C++/modules/Socket/SocketUdp.h Fri Jun 19 13:56:12 2015 +0200 @@ -43,7 +43,7 @@ * @return the number of bytes sent * @throw SocketError on error */ - inline unsigned sendto(const std::string &data, const SocketAddress &address) + inline unsigned sendto(const std::string &data, const std::unique_ptr<SocketAddress> &address) { return sendto(data.c_str(), data.length(), address); } @@ -56,7 +56,7 @@ * @return the string * @throw SocketError on error */ - inline std::string recvfrom(unsigned count, SocketAddress &info) + inline std::string recvfrom(unsigned count, std::unique_ptr<SocketAddress> &info) { std::string result; @@ -76,7 +76,7 @@ * @return the number of bytes received * @throw SocketError on error */ - virtual unsigned recvfrom(void *data, unsigned length, SocketAddress &info); + virtual unsigned recvfrom(void *data, unsigned length, std::unique_ptr<SocketAddress> &info); /** * Send data to an end point. @@ -87,7 +87,7 @@ * @return the number of bytes sent * @throw SocketError on error */ - virtual unsigned sendto(const void *data, unsigned length, const SocketAddress &address); + virtual unsigned sendto(const void *data, unsigned length, const std::unique_ptr<SocketAddress> &address); }; #endif // !_SOCKET_UDP_NG_H_
--- a/C++/tests/Socket/main.cpp Fri Jun 19 11:18:53 2015 +0200 +++ b/C++/tests/Socket/main.cpp Fri Jun 19 13:56:12 2015 +0200 @@ -64,7 +64,7 @@ TEST_F(TcpServerTest, connect) { m_tserver = std::thread([this] () { - m_server.bind(Internet("*", 16000, AF_INET)); + m_server.bind(std::make_unique<Internet>("*", 16000, AF_INET)); ASSERT_EQ(SocketState::Bound, m_server.state()); @@ -87,7 +87,7 @@ TEST_F(TcpServerTest, io) { m_tserver = std::thread([this] () { - m_server.bind(Internet("*", 16000, AF_INET)); + m_server.bind(std::make_unique<Internet>("*", 16000, AF_INET)); m_server.listen(); auto client = m_server.accept(); @@ -138,10 +138,9 @@ TEST_F(UdpServerTest, io) { m_tserver = std::thread([this] () { - SocketAddress info; + std::unique_ptr<SocketAddress> info = std::make_unique<Internet>("*", 16000, AF_INET); - m_server.bind(Internet("*", 16000, AF_INET)); - + m_server.bind(info); auto msg = m_server.recvfrom(512, info); ASSERT_EQ("hello world", msg); @@ -153,7 +152,7 @@ std::this_thread::sleep_for(100ms); m_tclient = std::thread([this] () { - Internet info("127.0.0.1", 16000, AF_INET); + std::unique_ptr<SocketAddress> info = std::make_unique<Internet>("127.0.0.1", 16000, AF_INET); m_client.sendto("hello world", info); @@ -397,7 +396,7 @@ , m_clientTcp{std::make_shared<SocketTcp>(AF_INET, 0)} { m_masterTcp->set(SOL_SOCKET, SO_REUSEADDR, 1); - m_masterTcp->bind(Internet("*", 16000, AF_INET)); + m_masterTcp->bind(std::make_unique<Internet>("*", 16000, AF_INET)); m_masterTcp->listen(); } @@ -498,7 +497,7 @@ TcpAcceptTest() { m_server.set(SOL_SOCKET, SO_REUSEADDR, 1); - m_server.bind(Internet("*", 16000, AF_INET)); + m_server.bind(std::make_unique<Internet>("*", 16000, AF_INET)); m_server.listen(); } @@ -527,7 +526,7 @@ TcpRecvTest() { m_server.set(SOL_SOCKET, SO_REUSEADDR, 1); - m_server.bind(Internet("*", 16000, AF_INET)); + m_server.bind(std::make_unique<Internet>("*", 16000, AF_INET)); m_server.listen(); }