#pragma once #if !defined(MIJIN_NET_SOCKET_HPP_INCLUDED) #define MIJIN_NET_SOCKET_HPP_INCLUDED 1 #include #include #include "../detect.hpp" #include "../async/coroutine.hpp" #include "../container/optional.hpp" #include "../io/stream.hpp" namespace mijin { // // public types // #if MIJIN_TARGET_OS == MIJIN_OS_WINDOWS using socket_handle_t = std::uintptr_t; inline constexpr socket_handle_t INVALID_SOCKET_HANDLE = static_cast(-1); #else using socket_handle_t = int; inline constexpr socket_handle_t INVALID_SOCKET_HANDLE = -1; #endif struct IPv4Address { std::array octets; auto operator<=>(const IPv4Address&) const noexcept = default; [[nodiscard]] static Optional fromString(std::string_view stringView) noexcept; }; struct IPv6Address { std::array hextets; auto operator<=>(const IPv6Address&) const noexcept = default; [[nodiscard]] static Optional fromString(std::string_view stringView) noexcept; }; using ip_address_t = std::variant; [[nodiscard]] inline Optional ipAddressFromString(std::string_view stringView) noexcept { if (Optional ipv4Address = IPv4Address::fromString(stringView); !ipv4Address.empty()) { return ip_address_t(*ipv4Address); } if (Optional ipv6Address = IPv6Address::fromString(stringView); !ipv6Address.empty()) { return ip_address_t(*ipv6Address); } return NULL_OPTIONAL; } class Socket { protected: Socket() noexcept = default; Socket(const Socket&) noexcept = default; Socket(Socket&&) noexcept = default; Socket& operator=(const Socket&) noexcept = default; Socket& operator=(Socket&&) noexcept = default; public: virtual ~Socket() noexcept = default; virtual Stream& getStream() noexcept = 0; }; class ServerSocket { protected: ServerSocket() noexcept = default; ServerSocket(const ServerSocket&) noexcept = default; ServerSocket(ServerSocket&&) noexcept = default; ServerSocket& operator=(const ServerSocket&) noexcept = default; ServerSocket& operator=(ServerSocket&&) noexcept = default; public: virtual ~ServerSocket() noexcept = default; virtual void close() noexcept = 0; virtual Task>> c_waitForConnection() noexcept = 0; }; class TCPStream : public Stream { private: socket_handle_t handle_ = INVALID_SOCKET_HANDLE; bool async_ = false; public: StreamError readRaw(std::span buffer, const ReadOptions& options, std::size_t* outBytesRead) override; StreamError writeRaw(std::span buffer) override; mijin::Task c_readRaw(std::span buffer, const ReadOptions& options, std::size_t *outBytesRead) override; mijin::Task c_writeRaw(std::span buffer) override; std::size_t tell() override; StreamError seek(std::intptr_t pos, SeekMode seekMode = SeekMode::ABSOLUTE) override; void flush() override; bool isAtEnd() override; StreamFeatures getFeatures() override; StreamError open(ip_address_t address, std::uint16_t port) noexcept; void close() noexcept; [[nodiscard]] bool isOpen() const noexcept { return handle_ != INVALID_SOCKET_HANDLE; } private: void setAsync(bool async); friend class TCPServerSocket; }; class TCPSocket : public Socket { private: TCPStream stream_; public: TCPStream& getStream() noexcept override; StreamError open(ip_address_t address, std::uint16_t port) noexcept { return stream_.open(address, port); } StreamError open(std::string_view addressText, std::uint16_t port) noexcept { if (Optional address = ipAddressFromString(addressText); !address.empty()) { return open(*address, port); } return StreamError::UNKNOWN_ERROR; } void close() noexcept { stream_.close(); } [[nodiscard]] bool isOpen() const noexcept { return stream_.isOpen(); } friend class TCPServerSocket; }; class TCPServerSocket : public ServerSocket { private: socket_handle_t handle_ = INVALID_SOCKET_HANDLE; public: StreamError setup(ip_address_t address, std::uint16_t port) noexcept; StreamError setup(std::string_view addressText, std::uint16_t port) noexcept { if (Optional address = ipAddressFromString(addressText); !address.empty()) { return setup(*address, port); } return StreamError::UNKNOWN_ERROR; } void close() noexcept override; [[nodiscard]] bool isListening() const noexcept { return handle_ >= 0; } Task>> c_waitForConnection() noexcept override; }; } #endif // !defined(MIJIN_NET_SOCKET_HPP_INCLUDED)