From 36f3c7a8ecf142aa77f08e331f06d90974e6d3e6 Mon Sep 17 00:00:00 2001 From: Andrew Golovashevich Date: Tue, 28 Oct 2025 11:00:59 +0300 Subject: [PATCH] Sockets template for berkeley's api and substitution for winsock2 --- CMakeLists.txt | 5 +- main.cpp | 40 +- modules/exceptions/src/windows.cppm | 7 + modules/sockets/CMakeLists.txt | 13 + modules/sockets/src/berkeley_sockets.cppm | 460 +++++++++++++++++++ modules/sockets/src/common.cppm | 15 + modules/sockets/src/platform/windows.cpp.inc | 271 +++++++++++ modules/sockets/src/platform/windows.hpp | 7 + modules/sockets/src/windows_binds.cppm | 13 + modules/streams/CMakeLists.txt | 11 + modules/streams/src/abstract.cppm | 52 +++ modules/threads/src/thread_routine.cppm | 2 +- 12 files changed, 881 insertions(+), 15 deletions(-) create mode 100644 modules/sockets/CMakeLists.txt create mode 100644 modules/sockets/src/berkeley_sockets.cppm create mode 100644 modules/sockets/src/common.cppm create mode 100644 modules/sockets/src/platform/windows.cpp.inc create mode 100644 modules/sockets/src/platform/windows.hpp create mode 100644 modules/sockets/src/windows_binds.cppm create mode 100644 modules/streams/CMakeLists.txt create mode 100644 modules/streams/src/abstract.cppm diff --git a/CMakeLists.txt b/CMakeLists.txt index 9280323..14f290b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,8 +19,11 @@ set(CMAKE_CXX_MODULE_STD 1) add_subdirectory(modules/exceptions) add_subdirectory(modules/threads) +add_subdirectory(modules/streams) +add_subdirectory(modules/sockets) + add_subdirectory(modules/asyncio) add_subdirectory(programs/lab4) add_executable(main main.cpp) -target_link_libraries(main PRIVATE exceptions threads) \ No newline at end of file +target_link_libraries(main PRIVATE exceptions threads sockets streams) \ No newline at end of file diff --git a/main.cpp b/main.cpp index b45cada..069b3d6 100644 --- a/main.cpp +++ b/main.cpp @@ -1,18 +1,32 @@ -#include - -import ru.landgrafhomyak.BGTU.networks_1.exceptions; +import std; import ru.landgrafhomyak.BGTU.networks_1.threads; +import ru.landgrafhomyak.BGTU.networks_1.sockets; int main() { - try { - auto t = LdH::fork("123", []() { std::cout << "123" << std::endl; }); - t.join(); - t.destroy(); - } catch (LdH::Exception const &e) { - std::cerr << "Uncaught exception in main:\n"; - e.printStackTrace(); - } catch (std::exception const &e) { - std::cerr << "Uncaught exception in main:\n" << e.what() << "\n"; - } + LdH::Sockets::init_sockets(); + + auto server = LdH::Sockets::listen_tcp(LdH::Sockets::IPv4Address::parse("127.0.0.1"), 8081, 1); + + auto server_thread = LdH::fork("server", [&] { + auto server_stream = server.wait_for_connection(); + char buffer[10]; + server_stream.sock.read(10, buffer); + std::cout << buffer << std::endl; + 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); + char buffer[10] = "hello\n"; + client.write(10, buffer); + client.close(); + }); + + server_thread.join(); + client_thread.join(); + server_thread.destroy(); + client_thread.destroy(); + server.close(); + + LdH::Sockets::deinit_sockets(); } diff --git a/modules/exceptions/src/windows.cppm b/modules/exceptions/src/windows.cppm index b866414..5532d4d 100644 --- a/modules/exceptions/src/windows.cppm +++ b/modules/exceptions/src/windows.cppm @@ -1,9 +1,14 @@ module; +#ifdef _WIN32 #include +#endif + export module ru.landgrafhomyak.BGTU.networks_1.exceptions.windows; +#ifdef _WIN32 + import std; import ru.landgrafhomyak.BGTU.networks_1.exceptions; @@ -49,3 +54,5 @@ void LdH::throwFromLastWindowsErrOrTimeout() { return; LdH::throwFromWindowsErrCode(err); } + +#endif diff --git a/modules/sockets/CMakeLists.txt b/modules/sockets/CMakeLists.txt new file mode 100644 index 0000000..862c36d --- /dev/null +++ b/modules/sockets/CMakeLists.txt @@ -0,0 +1,13 @@ +add_library(sockets STATIC) + +target_sources( + sockets + + PUBLIC + FILE_SET cxx_modules TYPE CXX_MODULES FILES + src/common.cppm + src/berkeley_sockets.cppm + src/windows_binds.cppm +) + +target_link_libraries(sockets PRIVATE wsock32 ws2_32 exceptions PUBLIC streams) \ No newline at end of file diff --git a/modules/sockets/src/berkeley_sockets.cppm b/modules/sockets/src/berkeley_sockets.cppm new file mode 100644 index 0000000..705f9d5 --- /dev/null +++ b/modules/sockets/src/berkeley_sockets.cppm @@ -0,0 +1,460 @@ +export module ru.landgrafhomyak.BGTU.networks_1.sockets:berkeley_sockets; + +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; } && + requires { typename ctx_t::sock_type_t; } && + requires { typename ctx_t::sock_type; } && + requires { { ctx_t::sock_type::stream() } -> std::same_as; } && + requires { { ctx_t::sock_type::dgram() } -> std::same_as; } && + requires { typename ctx_t::proto_t; } && + requires { typename ctx_t::proto; } && + 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::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) { { 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; }; + + 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, + }; + + _type_t _type; + + union { + struct { + } empty; + + ctx_t::ipv4 ipv4; + ctx_t::ipv6 ipv6; + } _value; + + public: + Address() : _type{_type_EMPTY}, _value{.empty{}} { + } + + Address(IPv4Address const &value) : _type{_type_IPv4}, _value{.ipv4 = value._value} { + } + + Address(IPv6Address const &value) : _type{_type_IPv6}, _value{.ipv6 = value._value} { + } + }; + + + enum _socket_state_t { + _socket_state_UNINITIALIZED, + _socket_state_MOVED, + _socket_state_AVAILABLE, + _socket_state_READING, + _socket_state_WRITING, + _socket_state_BOTH, + _socket_state_CLOSED, + }; + + template requires BerkeleySocketsContext + class _berkeley_socket_commons { + private: + std::atomic<_socket_state_t> _state; + + protected: + ctx_t::socket_t _value; + + explicit _berkeley_socket_commons(ctx_t::socket_t &&value) : _state{_socket_state_AVAILABLE}, _value{std::move(value)} { + } + + public: + _berkeley_socket_commons() : _state{_socket_state_UNINITIALIZED}, _value{} { + }; + + explicit _berkeley_socket_commons(_berkeley_socket_commons &&other) noexcept : _state{other._state.load()}, _value{std::move(other._value)} { + other._state.store(_socket_state_MOVED); + } + + _berkeley_socket_commons &operator=(_berkeley_socket_commons &&other) noexcept { + _socket_state_t current = this->_state.load(); + 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; + continue; + default: + LdH::abort("Variable already initialized"); + } + } + this->_state.store(other._state.exchange(_socket_state_MOVED)); + this->_value = std::move(other._value); + + return *this;; + } + + private: + _socket_state_t _cas_state(_socket_state_t expected, _socket_state_t next) { + this->_state.compare_exchange_strong(expected, next); + return expected; + } + + template<_socket_state_t exp1, _socket_state_t next1, _socket_state_t exp2, _socket_state_t next2> + _socket_state_t _cas_state_2() { + _socket_state_t current = this->_state.load(); + while (true) { + switch (current) { + case exp1: + if (this->_state.compare_exchange_weak(current, next1)) + return current; + continue; + case exp2: + if (this->_state.compare_exchange_weak(current, next2)) + return current; + continue; + default: + return current; + } + } + } + + protected: + void _start_reading(char const *busy_msg) { + switch (this->_cas_state_2<_socket_state_AVAILABLE, _socket_state_READING, _socket_state_WRITING, _socket_state_BOTH>()) { + case _socket_state_UNINITIALIZED: + LdH::abort("Socket not initialized"); + case _socket_state_MOVED: + LdH::abort("Socket was moved to another location"); + case _socket_state_AVAILABLE: + case _socket_state_WRITING: + break; + case _socket_state_READING: + case _socket_state_BOTH: + LdH::abort(busy_msg); + case _socket_state_CLOSED: + LdH::abort("Socket was closed"); + } + } + + void _finish_reading() { + this->_cas_state_2<_socket_state_READING, _socket_state_AVAILABLE, _socket_state_BOTH, _socket_state_WRITING>(); + } + + void _start_writing(char const *busy_msg) { + switch (this->_cas_state_2<_socket_state_AVAILABLE, _socket_state_WRITING, _socket_state_READING, _socket_state_BOTH>()) { + case _socket_state_UNINITIALIZED: + LdH::abort("Socket not initialized"); + case _socket_state_MOVED: + LdH::abort("Socket was moved to another location"); + case _socket_state_AVAILABLE: + case _socket_state_READING: + break; + case _socket_state_WRITING: + case _socket_state_BOTH: + LdH::abort(busy_msg); + case _socket_state_CLOSED: + LdH::abort("Socket was closed"); + } + } + + void _finish_writing() { + this->_cas_state_2<_socket_state_WRITING, _socket_state_AVAILABLE, _socket_state_BOTH, _socket_state_READING>(); + } + + void _close(char const *busy_msg) { + switch (this->_cas_state(_socket_state_AVAILABLE, _socket_state_CLOSED)) { + case _socket_state_UNINITIALIZED: + LdH::abort("Socket not initialized"); + case _socket_state_MOVED: + LdH::abort("Socket was moved to another location"); + case _socket_state_AVAILABLE: + break; + case _socket_state_READING: + case _socket_state_WRITING: + case _socket_state_BOTH: + LdH::abort(busy_msg); + case _socket_state_CLOSED: + LdH::abort("Socket already was closed"); + } + + this->_value.close(); + } + }; + + export + template requires BerkeleySocketsContext + class StreamSocketsServer; + + template + class _socket_internals { + }; + + + export + template requires BerkeleySocketsContext + class StreamSocket : public _berkeley_socket_commons, public virtual LdH::Streams::InputStream, public virtual LdH::Streams::OutputStream { + private: + explicit StreamSocket(ctx_t::socket_t &&value) : _berkeley_socket_commons{std::move(value)} { + } + + + template requires BerkeleySocketsContext + friend + class StreamSocketsServer; + + + template + friend + class _socket_internals; + + public: + StreamSocket() = default; + + explicit StreamSocket(StreamSocket &&other) noexcept = default; + + StreamSocket &operator=(StreamSocket &&other) noexcept = default; + + void read(std::size_t size, char *data) override { + this->_start_reading("Socket already read by another thread"); + this->_value.recv_stream(size, data); + this->_finish_reading(); + } + + void write(std::size_t size, char const *data) override { + this->_start_writing("Socket already written by another thread"); + this->_value.send_stream(size, data); + this->_finish_writing(); + } + + void close() override { + this->_close("Socket is busy with reading or writing"); + } + + ~StreamSocket() noexcept override = default; + }; + + + template + class _socket_internals > { + public: + static StreamSocket wrap(ctx_t::socket_t &&raw) { + return StreamSocket{std::move(raw)}; + } + }; + + + export + template + class SocketWithAddress { + public: + addr_t addr; + sock_t sock; + + SocketWithAddress(addr_t addr, sock_t &&sock) : addr{addr}, sock{std::move(sock)} { + } + }; + + template requires BerkeleySocketsContext + class StreamSocketsServer : public _berkeley_socket_commons { + private: + explicit StreamSocketsServer(ctx_t::socket_t &&value) : _berkeley_socket_commons{std::move(value)} { + } + + template + friend + class _socket_internals; + + public: + StreamSocketsServer() = default; + + explicit StreamSocketsServer(StreamSocketsServer &&other) noexcept = default; + + StreamSocketsServer &operator=(StreamSocketsServer &&other) noexcept = default; + + [[nodiscard]] + SocketWithAddress > wait_for_connection() { + this->_start_reading("Socket already waiting for connections"); + + typename _addr_internals::raw_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)}}; + } + + void close() { + this->_close("Socket busy with waiting for connection"); + } + + ~StreamSocketsServer() noexcept = default; + }; + + + template + class _socket_internals > { + public: + static StreamSocketsServer wrap(ctx_t::socket_t &&raw) { + return StreamSocketsServer{std::move(raw)}; + } + }; + + export + template requires BerkeleySocketsContext + [[nodiscard]] + StreamSocket connect_tcp(addr_t addr, std::uint_least16_t port) { + 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); + return _socket_internals >::wrap(std::move(sock)); + } + + export + 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)) + 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); + sock.listen(queue_size); + return _socket_internals >::wrap(std::move(sock)); + } +} diff --git a/modules/sockets/src/common.cppm b/modules/sockets/src/common.cppm new file mode 100644 index 0000000..abd6f17 --- /dev/null +++ b/modules/sockets/src/common.cppm @@ -0,0 +1,15 @@ +module; + + + +#if defined(_WIN32) + +# include "platform/windows.hpp" + +export module ru.landgrafhomyak.BGTU.networks_1.sockets; + +# include "platform/windows.cpp.inc" + +#endif + +module:private; \ No newline at end of file diff --git a/modules/sockets/src/platform/windows.cpp.inc b/modules/sockets/src/platform/windows.cpp.inc new file mode 100644 index 0000000..53cea8f --- /dev/null +++ b/modules/sockets/src/platform/windows.cpp.inc @@ -0,0 +1,271 @@ +import std; +import ru.landgrafhomyak.BGTU.networks_1.exceptions; +import ru.landgrafhomyak.BGTU.networks_1.exceptions.windows; +import ru.landgrafhomyak.BGTU.networks_1.streams; +import :berkeley_sockets; + +namespace LdH::Sockets { + export + void init_sockets() { + WORD wVersionRequested = MAKEWORD(2, 2); + WSADATA wsaData; + if (0 != WSAStartup(wVersionRequested, &wsaData)) + LdH::throwFromWindowsErrCode(WSAGetLastError()); + } + + export + void deinit_sockets() { + WSACleanup(); + } + + + 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; + }; + + using af_t = int; + using sock_type_t = int; + using proto_t = int; + + struct af { + public: + static int inet() { return AF_INET; } + static int inet6() { return AF_INET6; } + static int ipx() { return AF_IPX; } + }; + + struct sock_type { + public: + static int stream() { return SOCK_STREAM; } + static int dgram() { return SOCK_DGRAM; } + }; + + struct proto { + public: + static int tcp() { return IPPROTO_TCP; } + static int udp() { return IPPROTO_UDP; } + static int icmp() { return IPPROTO_ICMP; } + }; + + class socket_t { + private: + SOCKET _value; + + explicit socket_t(SOCKET value) : _value{value} { + } + + public: + socket_t() : _value{INVALID_SOCKET} { + } + + socket_t(socket_t &&other) noexcept : _value{other._value} { + other._value = INVALID_SOCKET; + } + + socket_t &operator=(socket_t &&other) noexcept { + this->_value = other._value; + other._value = INVALID_SOCKET; + return *this; + } + + ~socket_t() = default; + + static socket_t create(int af, int type, int proto) { + SOCKET sock = ::socket(af, type, proto); + if (sock == INVALID_SOCKET) { + LdH::throwFromWindowsErrCode(WSAGetLastError()); + } + + return socket_t{sock}; + } + + void close() { + if (0 != closesocket(this->_value)) { + LdH::throwFromWindowsErrCode(WSAGetLastError()); + } + 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))) + 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))) + 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); + 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()); + } + } + + + void send_stream(std::size_t size, char const *data) { + while (true) { + std::size_t sent_count = ::send(this->_value, data, size, 0); + if (sent_count == SOCKET_ERROR) { + LdH::throwFromWindowsErrCode(WSAGetLastError()); + } + data += sent_count; + size -= sent_count; + if (size <= 0) return; + } + } + + void recv_stream(std::size_t size, char *data) { + while (true) { + std::size_t sent_count = ::recv(this->_value, data, size, 0); + if (sent_count == SOCKET_ERROR) { + LdH::throwFromWindowsErrCode(WSAGetLastError()); + } + data += sent_count; + size -= sent_count; + if (size <= 0) return; + } + } + }; + }; + + static_assert(Berkeley::BerkeleySocketsContext<_WinsockContext>); + + + export using IPv4Address = Berkeley::IPv4Address<_WinsockContext>; + export using IPv6Address = Berkeley::IPv6Address<_WinsockContext>; + + export + template + using SocketWithAddress = Berkeley::SocketWithAddress; + + export using StreamSocket = Berkeley::StreamSocket<_WinsockContext>; + + export + template + using StreamSocketsServer = Berkeley::StreamSocketsServer<_WinsockContext, addr_t>; + + export + StreamSocket connect_tcp(IPv4Address addr, std::uint_least16_t port) { + return Berkeley::connect_tcp<_WinsockContext, IPv4Address>(addr, port); + } + + 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); + } +} diff --git a/modules/sockets/src/platform/windows.hpp b/modules/sockets/src/platform/windows.hpp new file mode 100644 index 0000000..bfdae42 --- /dev/null +++ b/modules/sockets/src/platform/windows.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include +#include +#include +#include + diff --git a/modules/sockets/src/windows_binds.cppm b/modules/sockets/src/windows_binds.cppm new file mode 100644 index 0000000..9a6147f --- /dev/null +++ b/modules/sockets/src/windows_binds.cppm @@ -0,0 +1,13 @@ +module; + +#include +#include +#include + +export module ru.landgrafhomyak.BGTU.networks_1.sockets:binds; + +namespace LdH::Sockets::_Binds { + export using ::sockaddr_in; + export using ::sockaddr_in6; + export using ::SOCKET; +} diff --git a/modules/streams/CMakeLists.txt b/modules/streams/CMakeLists.txt new file mode 100644 index 0000000..ef38b16 --- /dev/null +++ b/modules/streams/CMakeLists.txt @@ -0,0 +1,11 @@ +add_library(streams STATIC) + +target_sources( + streams + + PUBLIC + FILE_SET cxx_modules TYPE CXX_MODULES FILES + src/abstract.cppm +) + +#target_link_libraries(streams PRIVATE ) \ No newline at end of file diff --git a/modules/streams/src/abstract.cppm b/modules/streams/src/abstract.cppm new file mode 100644 index 0000000..2d46d60 --- /dev/null +++ b/modules/streams/src/abstract.cppm @@ -0,0 +1,52 @@ +export module ru.landgrafhomyak.BGTU.networks_1.streams; + +import std; + +namespace LdH::Streams { + export class OutputStream { + public: + virtual void write(std::size_t, char const *) = 0; + + virtual void close() = 0; + + virtual ~OutputStream() noexcept = 0; + }; + + export class InputStream { + public: + virtual void read(std::size_t, char *) = 0; + + virtual void close() = 0; + + virtual ~InputStream() noexcept = 0; + }; + + export class OutputMessanger { + public: + virtual void sendOne(std::size_t, char const *) = 0; + + virtual void close() = 0; + + virtual ~OutputMessanger() noexcept = 0; + }; + + + export class InputMessanger { + public: + virtual void recvOneTruncating(std::size_t, char *) = 0; + + virtual void close() = 0; + + virtual ~InputMessanger() noexcept = 0; + }; +} + +module: private; + +inline LdH::Streams::OutputStream::~OutputStream() = default; + +inline LdH::Streams::InputStream::~InputStream() = default; + +inline LdH::Streams::OutputMessanger::~OutputMessanger() = default; + +inline LdH::Streams::InputMessanger::~InputMessanger() = default; diff --git a/modules/threads/src/thread_routine.cppm b/modules/threads/src/thread_routine.cppm index ba682c4..f6dfc9d 100644 --- a/modules/threads/src/thread_routine.cppm +++ b/modules/threads/src/thread_routine.cppm @@ -4,5 +4,5 @@ import std; namespace LdH { export template - concept ThreadRoutine = std::invocable && std::same_as, void> && std::movable && std::destructible; + concept ThreadRoutine = std::invocable && std::same_as, void> /*&& std::movable*/ && std::destructible; }