From 8ce26c24c10ea12333743006aa9bd136a3e1839b Mon Sep 17 00:00:00 2001 From: Andrew Golovashevich Date: Tue, 28 Oct 2025 12:33:43 +0300 Subject: [PATCH] Replaced types for each address family with union --- main.cpp | 6 +- modules/sockets/src/berkeley_sockets.cppm | 251 ++++++------------- modules/sockets/src/platform/windows.cpp.inc | 183 +++++--------- 3 files changed, 139 insertions(+), 301 deletions(-) diff --git a/main.cpp b/main.cpp index 069b3d6..81f1986 100644 --- a/main.cpp +++ b/main.cpp @@ -6,7 +6,9 @@ import ru.landgrafhomyak.BGTU.networks_1.sockets; int main() { LdH::Sockets::init_sockets(); - auto server = LdH::Sockets::listen_tcp(LdH::Sockets::IPv4Address::parse("127.0.0.1"), 8081, 1); + std::cout << LdH::Sockets::Address::parse("google.com", "443").to_string() << std::endl; + + auto server = LdH::Sockets::listen_tcp(LdH::Sockets::Address::parse("127.0.0.1", "8081"), 1); auto server_thread = LdH::fork("server", [&] { auto server_stream = server.wait_for_connection(); @@ -16,7 +18,7 @@ int main() { server_stream.sock.close(); }); auto client_thread = LdH::fork("client", [&] { - auto client = LdH::Sockets::connect_tcp(LdH::Sockets::IPv4Address::parse("127.0.0.1"), 8081); + auto client = LdH::Sockets::connect_tcp(LdH::Sockets::Address::parse("127.0.0.1", "8081")); char buffer[10] = "hello\n"; client.write(10, buffer); client.close(); diff --git a/modules/sockets/src/berkeley_sockets.cppm b/modules/sockets/src/berkeley_sockets.cppm index 705f9d5..afab237 100644 --- a/modules/sockets/src/berkeley_sockets.cppm +++ b/modules/sockets/src/berkeley_sockets.cppm @@ -4,31 +4,13 @@ import std; import ru.landgrafhomyak.BGTU.networks_1.exceptions; import ru.landgrafhomyak.BGTU.networks_1.streams; - namespace LdH::Sockets::Berkeley { - export - struct sized_ptr { - std::size_t size; - void *ptr; - }; - - template - concept _raw_addr = std::default_initializable && std::destructible && - std::copyable && std::movable && - requires { { addr_t::any() } -> std::same_as; } && - requires(char const *raw) { { addr_t::parse(raw) } -> std::same_as; } && - requires(addr_t self) { { self.raw_ptr() } -> std::same_as; } && - requires { { addr_t::af_value() } -> std::same_as; }; - - export template - concept BerkeleySocketsContext = requires { typename ctx_t::addr; } && - requires { typename ctx_t::af_t; } && - requires { typename ctx_t::af; } && - requires { { ctx_t::af::inet() } -> std::same_as; } && - requires { { ctx_t::af::inet6() } -> std::same_as; } && - requires { { ctx_t::af::ipx() } -> std::same_as; } && + concept BerkeleySocketsContext = requires { typename ctx_t::address_t; } && + std::default_initializable && std::copyable && std::destructible && + requires(char const *h, char const *s) { { ctx_t::address_t::parse(h, s) } -> std::same_as; } && + requires(typename ctx_t::address_t s) { { s.to_string() } -> std::same_as; } && requires { typename ctx_t::sock_type_t; } && requires { typename ctx_t::sock_type; } && requires { { ctx_t::sock_type::stream() } -> std::same_as; } && @@ -38,143 +20,72 @@ namespace LdH::Sockets::Berkeley { requires { { ctx_t::proto::tcp() } -> std::same_as; } && requires { { ctx_t::proto::udp() } -> std::same_as; } && requires { { ctx_t::proto::icmp() } -> std::same_as; } && - requires { typename ctx_t::addr::ipv4; requires _raw_addr; } && - requires { typename ctx_t::addr::ipv6; requires _raw_addr; } && requires { typename ctx_t::socket_t; } && std::default_initializable && std::movable && std::destructible && - requires(typename ctx_t::af_t a, typename ctx_t::sock_type_t t, typename ctx_t::proto_t p) { { ctx_t::socket_t::create(a, t, p) } -> std::same_as; } && + requires(typename ctx_t::address_t a, typename ctx_t::sock_type_t t, typename ctx_t::proto_t p) { { ctx_t::socket_t::create(a, t, p) } -> std::same_as; } && requires(typename ctx_t::socket_t s, std::size_t q) { { s.listen(q) } -> std::same_as; } && - requires(typename ctx_t::socket_t s, typename ctx_t::addr::ipv4 a, std::uint_least16_t p) { { s.connect(a, p) } -> std::same_as; } && - requires(typename ctx_t::socket_t s, typename ctx_t::addr::ipv6 a, std::uint_least16_t p) { { s.connect(a, p) } -> std::same_as; } && - requires(typename ctx_t::socket_t s, typename ctx_t::addr::ipv4 a, std::uint_least16_t p) { { s.bind(a, p) } -> std::same_as; } && - requires(typename ctx_t::socket_t s, typename ctx_t::addr::ipv6 a, std::uint_least16_t p) { { s.bind(a, p) } -> std::same_as; } && + requires(typename ctx_t::socket_t s, typename ctx_t::address_t e) { { s.connect(e) } -> std::same_as; } && + requires(typename ctx_t::socket_t s, typename ctx_t::address_t e) { { s.bind(e) } -> std::same_as; } && requires(typename ctx_t::socket_t s) { { s.close() } -> std::same_as; } && requires(typename ctx_t::socket_t s, std::size_t c, char const *d) { { s.send_stream(c, d) } -> std::same_as; } && requires(typename ctx_t::socket_t s, std::size_t c, char *d) { { s.recv_stream(c, d) } -> std::same_as; } && - requires(typename ctx_t::socket_t s, typename ctx_t::addr::ipv4 *a) { { s.accept(a) } -> std::same_as; } && - requires(typename ctx_t::socket_t s, typename ctx_t::addr::ipv6 *a) { { s.accept(a) } -> std::same_as; }; + requires(typename ctx_t::socket_t s, typename ctx_t::address_t *a) { { s.accept(a) } -> std::same_as; }; + + template requires BerkeleySocketsContext + class _addr_internals; export - template requires BerkeleySocketsContext - class Address; - - template - class _addr_internals { - }; - - template - class _addr_wrapper { - private: - bool _has_value; - raw_t _value; - - _addr_wrapper(bool has_value, raw_t value) : _has_value{has_value}, _value{value} { - } - - template requires BerkeleySocketsContext - friend - class Address; - - template - friend - class _addr_internals; - - public: - _addr_wrapper() : _has_value{false}, _value{} { - } - - _addr_wrapper(_addr_wrapper const &other) noexcept = default; - - _addr_wrapper(_addr_wrapper &&other) noexcept : _has_value{other._has_value}, _value{other._value} { - other._has_value = false; - } - - _addr_wrapper &operator=(_addr_wrapper const &other) = default; - - _addr_wrapper &operator=(_addr_wrapper &&other) noexcept { - this->_has_value = other._has_value; - other._has_value = false; - this->_value = std::move(other._value); - return *this; - } - - ~_addr_wrapper() noexcept = default; - - static _addr_wrapper parse(char const *raw) { - return {true, raw_t::parse(raw)}; - } - - static _addr_wrapper parse(std::string const &raw) { - return {true, raw_t::parse(raw.c_str())}; - } - - static _addr_wrapper any() { - return _addr_wrapper{raw_t::any()}; - } - }; - - - export - template requires BerkeleySocketsContext - using IPv4Address = _addr_wrapper; - - export - template requires BerkeleySocketsContext - using IPv6Address = _addr_wrapper; - - - template - class _addr_internals<_addr_wrapper<_raw_addr_t> > { - public: - using raw_t = _raw_addr_t; - - static _addr_wrapper<_raw_addr_t> empty() { - return _addr_wrapper<_raw_addr_t>{false, _raw_addr_t{}}; - } - - static _addr_wrapper<_raw_addr_t> wrap(_raw_addr_t value) { - return _addr_wrapper<_raw_addr_t>{true, value}; - } - - static _raw_addr_t raw_of(_addr_wrapper<_raw_addr_t> const &value) { - return value._value; - } - - static bool has_value(_addr_wrapper<_raw_addr_t> const &value) { - return value._has_value; - } - }; - template requires BerkeleySocketsContext class Address { private: - enum _type_t { - _type_EMPTY, - _type_IPv4, - _type_IPv6, - }; + bool _has_value; + ctx_t::address_t _value; - _type_t _type; + explicit Address(bool has_value, ctx_t::address_t value) : _has_value{has_value}, _value{value} { + } - union { - struct { - } empty; - - ctx_t::ipv4 ipv4; - ctx_t::ipv6 ipv6; - } _value; + template requires BerkeleySocketsContext + friend class _addr_internals; public: - Address() : _type{_type_EMPTY}, _value{.empty{}} { + Address() : _has_value{false}, _value{} { } - Address(IPv4Address const &value) : _type{_type_IPv4}, _value{.ipv4 = value._value} { + Address(Address const &other) noexcept = default; + + Address(Address &&other) noexcept : _has_value{other._has_value}, _value{other._value} { + other._has_value = false; } - Address(IPv6Address const &value) : _type{_type_IPv6}, _value{.ipv6 = value._value} { + Address &operator=(Address const &other) = default; + + Address &operator=(Address &&other) noexcept { + this->_has_value = other._has_value; + this->_value = std::move(other._value); + other._has_value = false; + return *this; + } + + ~Address() { + this->_has_value = false; + } + + static Address parse(char const *host, char const *service) { + return Address{true, ctx_t::address_t::parse(host, service)}; + } + + std::string to_string() { + return this->_value.to_string(); } }; + template requires BerkeleySocketsContext + class _addr_internals { + public: + static bool has_value(Address const &v) { return v._has_value; } + static ctx_t::address_t unwrap(Address const &v) { return v._value; } + static Address wrap(ctx_t::address_t const &raw) { return Address{true, raw}; } + }; enum _socket_state_t { _socket_state_UNINITIALIZED, @@ -210,25 +121,21 @@ namespace LdH::Sockets::Berkeley { while (true) { switch (current) { case _socket_state_UNINITIALIZED: - if (this->_state.compare_exchange_weak(current, _socket_state_AVAILABLE)) - return current; - continue; case _socket_state_MOVED: - if (this->_state.compare_exchange_weak(current, _socket_state_AVAILABLE)) - return current; - continue; case _socket_state_CLOSED: if (this->_state.compare_exchange_weak(current, _socket_state_AVAILABLE)) - return current; + goto DONE; continue; default: LdH::abort("Variable already initialized"); } } + + DONE: this->_state.store(other._state.exchange(_socket_state_MOVED)); this->_value = std::move(other._value); - return *this;; + return *this; } private: @@ -320,10 +227,10 @@ namespace LdH::Sockets::Berkeley { }; export - template requires BerkeleySocketsContext + template requires BerkeleySocketsContext class StreamSocketsServer; - template + template class _socket_internals { }; @@ -336,7 +243,7 @@ namespace LdH::Sockets::Berkeley { } - template requires BerkeleySocketsContext + template requires BerkeleySocketsContext friend class StreamSocketsServer; @@ -348,7 +255,7 @@ namespace LdH::Sockets::Berkeley { public: StreamSocket() = default; - explicit StreamSocket(StreamSocket &&other) noexcept = default; + StreamSocket(StreamSocket &&other) noexcept = default; StreamSocket &operator=(StreamSocket &&other) noexcept = default; @@ -382,17 +289,17 @@ namespace LdH::Sockets::Berkeley { export - template + template class SocketWithAddress { public: - addr_t addr; + Address addr; sock_t sock; - SocketWithAddress(addr_t addr, sock_t &&sock) : addr{addr}, sock{std::move(sock)} { + SocketWithAddress(Address addr, sock_t &&sock) : addr{addr}, sock{std::move(sock)} { } }; - template requires BerkeleySocketsContext + template requires BerkeleySocketsContext class StreamSocketsServer : public _berkeley_socket_commons { private: explicit StreamSocketsServer(ctx_t::socket_t &&value) : _berkeley_socket_commons{std::move(value)} { @@ -405,18 +312,18 @@ namespace LdH::Sockets::Berkeley { public: StreamSocketsServer() = default; - explicit StreamSocketsServer(StreamSocketsServer &&other) noexcept = default; + StreamSocketsServer(StreamSocketsServer &&other) noexcept = default; StreamSocketsServer &operator=(StreamSocketsServer &&other) noexcept = default; [[nodiscard]] - SocketWithAddress > wait_for_connection() { + SocketWithAddress > wait_for_connection() { this->_start_reading("Socket already waiting for connections"); - typename _addr_internals::raw_t addr{}; + typename ctx_t::address_t addr{}; typename ctx_t::socket_t raw = this->_value.accept(&addr); this->_finish_reading(); - return SocketWithAddress >{_addr_internals::wrap(addr), StreamSocket{std::move(raw)}}; + return SocketWithAddress >{_addr_internals::wrap(addr), _socket_internals >::wrap(std::move(raw))}; } void close() { @@ -427,34 +334,34 @@ namespace LdH::Sockets::Berkeley { }; - template - class _socket_internals > { + template + class _socket_internals > { public: - static StreamSocketsServer wrap(ctx_t::socket_t &&raw) { - return StreamSocketsServer{std::move(raw)}; + static StreamSocketsServer wrap(ctx_t::socket_t &&raw) { + return StreamSocketsServer{std::move(raw)}; } }; export - template requires BerkeleySocketsContext + template requires BerkeleySocketsContext [[nodiscard]] - StreamSocket connect_tcp(addr_t addr, std::uint_least16_t port) { - if (!_addr_internals::has_value(addr)) + StreamSocket connect_tcp(Address addr) { + if (!_addr_internals::has_value(addr)) LdH::abort("Address not initialized"); - typename ctx_t::socket_t sock = ctx_t::socket_t::create(_addr_internals::raw_t::af_value(), ctx_t::sock_type::stream(), ctx_t::proto::tcp()); - sock.connect(_addr_internals::raw_of(addr), port); + typename ctx_t::socket_t sock = ctx_t::socket_t::create(_addr_internals::unwrap(addr), ctx_t::sock_type::stream(), ctx_t::proto::tcp()); + sock.connect(_addr_internals::unwrap(addr)); return _socket_internals >::wrap(std::move(sock)); } export - template requires BerkeleySocketsContext + template requires BerkeleySocketsContext [[nodiscard]] - StreamSocketsServer listen_tcp(addr_t addr, std::uint_least16_t port, std::size_t queue_size) { - if (!_addr_internals::has_value(addr)) + StreamSocketsServer listen_tcp(Address addr, std::size_t queue_size) { + if (!_addr_internals::has_value(addr)) LdH::abort("Address not initialized"); - typename ctx_t::socket_t sock = ctx_t::socket_t::create(_addr_internals::raw_t::af_value(), ctx_t::sock_type::stream(), ctx_t::proto::tcp()); - sock.bind(_addr_internals::raw_of(addr), port); + typename ctx_t::socket_t sock = ctx_t::socket_t::create(_addr_internals::unwrap(addr), ctx_t::sock_type::stream(), ctx_t::proto::tcp()); + sock.bind(_addr_internals::unwrap(addr)); sock.listen(queue_size); - return _socket_internals >::wrap(std::move(sock)); + return _socket_internals >::wrap(std::move(sock)); } } diff --git a/modules/sockets/src/platform/windows.cpp.inc b/modules/sockets/src/platform/windows.cpp.inc index 53cea8f..2b44bb1 100644 --- a/modules/sockets/src/platform/windows.cpp.inc +++ b/modules/sockets/src/platform/windows.cpp.inc @@ -19,57 +19,49 @@ namespace LdH::Sockets { } - template - class _in_addr_wrapper { - public: - _value_t _value; - - private: - _in_addr_wrapper(_value_t v) : _value{v} { - } - - public: - _in_addr_wrapper() = default; - - _in_addr_wrapper(_in_addr_wrapper const &other) = default; - - _in_addr_wrapper(_in_addr_wrapper &&other) = default; - - _in_addr_wrapper &operator=(_in_addr_wrapper const &other) = default; - - _in_addr_wrapper &operator=(_in_addr_wrapper &&other) = default; - - static _in_addr_wrapper any() { return _in_addr_wrapper{_any_constructor()}; } - - static _in_addr_wrapper parse(char const *raw) { - _value_t val; - switch (inet_pton(_family, raw, &val)) { - case 1: - break; - case 0: - throw LdH::Exception{"Wrong IP address value"}; - default: - LdH::throwFromWindowsErrCode(WSAGetLastError()); - break; - } - return _in_addr_wrapper{val}; - } - - Berkeley::sized_ptr raw_ptr() { - return {sizeof(_value_t), &(this->_value)}; - } - - static INT af_value() { return _family; }; - }; - - struct _WinsockContext { - struct addr { - using ipv4 = _in_addr_wrapper; - using ipv6 = _in_addr_wrapper; + class address_t { + public: + sockaddr_storage _value; + + private: + address_t(sockaddr_storage value) : _value{value} { + } + + public: + address_t() = default; + + address_t(address_t const &) = default; + + address_t(address_t &&) = default; + + address_t &operator=(address_t const &) = default; + + address_t &operator=(address_t &&) = default; + + static address_t parse(char const *addr, char const *service) { + addrinfo *p; + if (0 != getaddrinfo(addr, service, nullptr, &p)) { + LdH::throwFromWindowsErrCode(WSAGetLastError()); + } + sockaddr_storage out; + ZeroMemory(&out, sizeof(sockaddr_storage)); + CopyMemory(&out, p->ai_addr, p->ai_addrlen); + freeaddrinfo(p); + return address_t{out}; + } + + std::string to_string() { + char buffer[1024]; + DWORD buf_size = 1024; + if (0 != WSAAddressToStringA(reinterpret_cast(&(this->_value)), sizeof(sockaddr_storage), nullptr, buffer, &buf_size)) { + LdH::throwFromWindowsErrCode(WSAGetLastError()); + } + + return std::string{buffer, buf_size - 1}; + } }; - using af_t = int; using sock_type_t = int; using proto_t = int; @@ -116,8 +108,8 @@ namespace LdH::Sockets { ~socket_t() = default; - static socket_t create(int af, int type, int proto) { - SOCKET sock = ::socket(af, type, proto); + static socket_t create(address_t const &addr, int type, int proto) { + SOCKET sock = ::socket(addr._value.ss_family, type, proto); if (sock == INVALID_SOCKET) { LdH::throwFromWindowsErrCode(WSAGetLastError()); } @@ -132,74 +124,24 @@ namespace LdH::Sockets { this->_value = INVALID_SOCKET; } - void connect(addr::ipv4 addr, std::uint_least16_t port) { - sockaddr_in combined; - ZeroMemory(&combined, sizeof(combined)); - combined.sin_family = AF_INET; - combined.sin_port = htons(port); - combined.sin_addr = addr._value; - - if (0 != ::connect(this->_value, reinterpret_cast(&combined), sizeof(combined))) + void connect(address_t addr) { + if (0 != ::connect(this->_value, reinterpret_cast(&(addr._value)), sizeof(sockaddr))) LdH::throwFromWindowsErrCode(WSAGetLastError()); } - void connect(addr::ipv6 addr, std::uint_least16_t port) { - sockaddr_in6 combined; - ZeroMemory(&combined, sizeof(combined)); - combined.sin6_family = AF_INET6; - combined.sin6_port = htons(port); - combined.sin6_addr = addr._value; - - if (0 != ::connect(this->_value, reinterpret_cast(&combined), sizeof(combined))) + void bind(address_t addr) { + if (0 != ::bind(this->_value, reinterpret_cast(&(addr._value)), sizeof(sockaddr))) LdH::throwFromWindowsErrCode(WSAGetLastError()); } - void bind(addr::ipv4 addr, std::uint_least16_t port) { - sockaddr_in combined; - ZeroMemory(&combined, sizeof(combined)); - combined.sin_family = AF_INET; - combined.sin_port = htons(port); - combined.sin_addr = addr._value; - - if (0 != ::bind(this->_value, reinterpret_cast(&combined), sizeof(combined))) - LdH::throwFromWindowsErrCode(WSAGetLastError()); - } - - void bind(addr::ipv6 addr, std::uint_least16_t port) { - sockaddr_in6 combined; - ZeroMemory(&combined, sizeof(combined)); - combined.sin6_family = AF_INET6; - combined.sin6_port = htons(port); - combined.sin6_addr = addr._value; - - if (0 != ::bind(this->_value, reinterpret_cast(&combined), sizeof(combined))) - LdH::throwFromWindowsErrCode(WSAGetLastError()); - } - - socket_t accept(addr::ipv4 *addr) { - sockaddr_in raw_addr; - raw_addr.sin_family = AF_INET; - int size_i = sizeof(sockaddr_in); - SOCKET sock = ::accept(this->_value, reinterpret_cast(&raw_addr), &size_i); + socket_t accept(address_t *addr) { + SOCKET sock = ::accept(this->_value, reinterpret_cast(&(addr->_value)), nullptr); if (sock == INVALID_SOCKET) { LdH::throwFromWindowsErrCode(WSAGetLastError()); } - addr->_value = raw_addr.sin_addr; return socket_t{sock}; } - socket_t accept(addr::ipv6 *addr) { - sockaddr_in6 raw_addr; - int size_i = sizeof(sockaddr_in6); - SOCKET sock = ::accept(this->_value, reinterpret_cast(&raw_addr), &size_i); - if (sock == INVALID_SOCKET) { - LdH::throwFromWindowsErrCode(WSAGetLastError()); - } - addr->_value = raw_addr.sin6_addr; - return socket_t{sock}; - } - - void listen(std::size_t queue_size) { if (0 != ::listen(this->_value, queue_size)) { LdH::throwFromWindowsErrCode(WSAGetLastError()); @@ -236,36 +178,23 @@ namespace LdH::Sockets { static_assert(Berkeley::BerkeleySocketsContext<_WinsockContext>); - export using IPv4Address = Berkeley::IPv4Address<_WinsockContext>; - export using IPv6Address = Berkeley::IPv6Address<_WinsockContext>; + export using Address = Berkeley::Address<_WinsockContext>; export - template - using SocketWithAddress = Berkeley::SocketWithAddress; + template + using SocketWithAddress = Berkeley::SocketWithAddress<_WinsockContext, sock_t>; export using StreamSocket = Berkeley::StreamSocket<_WinsockContext>; - export - template - using StreamSocketsServer = Berkeley::StreamSocketsServer<_WinsockContext, addr_t>; + export using StreamSocketsServer = Berkeley::StreamSocketsServer<_WinsockContext>; export - StreamSocket connect_tcp(IPv4Address addr, std::uint_least16_t port) { - return Berkeley::connect_tcp<_WinsockContext, IPv4Address>(addr, port); + StreamSocket connect_tcp(Address addr) { + return Berkeley::connect_tcp<_WinsockContext>(addr); } export - StreamSocket connect_tcp(IPv6Address addr, std::uint_least16_t port) { - return Berkeley::connect_tcp<_WinsockContext, IPv6Address>(addr, port); - } - - export - StreamSocketsServer listen_tcp(IPv4Address addr, std::uint_least16_t port, std::size_t queue_size) { - return Berkeley::listen_tcp<_WinsockContext, IPv4Address>(addr, port, queue_size); - } - - export - StreamSocketsServer listen_tcp(IPv6Address addr, std::uint_least16_t port, std::size_t queue_size) { - return Berkeley::listen_tcp<_WinsockContext, IPv6Address>(addr, port, queue_size); + StreamSocketsServer listen_tcp(Address addr, std::size_t queue_size) { + return Berkeley::listen_tcp<_WinsockContext>(addr, queue_size); } }