166 lines
4.8 KiB
C++
166 lines
4.8 KiB
C++
|
|
#pragma once
|
|
|
|
#if !defined(MIJIN_NET_SOCKET_HPP_INCLUDED)
|
|
#define MIJIN_NET_SOCKET_HPP_INCLUDED 1
|
|
|
|
#include <array>
|
|
#include <variant>
|
|
#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<socket_handle_t>(-1);
|
|
#else
|
|
using socket_handle_t = int;
|
|
|
|
inline constexpr socket_handle_t INVALID_SOCKET_HANDLE = -1;
|
|
#endif
|
|
|
|
struct IPv4Address
|
|
{
|
|
std::array<std::uint8_t, 4> octets;
|
|
|
|
auto operator<=>(const IPv4Address&) const noexcept = default;
|
|
|
|
[[nodiscard]]
|
|
static Optional<IPv4Address> fromString(std::string_view stringView) noexcept;
|
|
};
|
|
|
|
struct IPv6Address
|
|
{
|
|
std::array<std::uint16_t, 8> hextets;
|
|
|
|
auto operator<=>(const IPv6Address&) const noexcept = default;
|
|
|
|
[[nodiscard]]
|
|
static Optional<IPv6Address> fromString(std::string_view stringView) noexcept;
|
|
};
|
|
using ip_address_t = std::variant<IPv4Address, IPv6Address>;
|
|
|
|
[[nodiscard]]
|
|
inline Optional<ip_address_t> ipAddressFromString(std::string_view stringView) noexcept
|
|
{
|
|
if (Optional<IPv4Address> ipv4Address = IPv4Address::fromString(stringView); !ipv4Address.empty())
|
|
{
|
|
return ip_address_t(*ipv4Address);
|
|
}
|
|
if (Optional<IPv6Address> 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<StreamResult<std::unique_ptr<Socket>>> c_waitForConnection() noexcept = 0;
|
|
};
|
|
|
|
class TCPStream : public Stream
|
|
{
|
|
private:
|
|
socket_handle_t handle_ = INVALID_SOCKET_HANDLE;
|
|
bool async_ = false;
|
|
public:
|
|
StreamError readRaw(std::span<std::uint8_t> buffer, const ReadOptions& options, std::size_t* outBytesRead) override;
|
|
StreamError writeRaw(std::span<const std::uint8_t> buffer) override;
|
|
mijin::Task<StreamError> c_readRaw(std::span<std::uint8_t> buffer, const ReadOptions& options, std::size_t *outBytesRead) override;
|
|
mijin::Task<StreamError> c_writeRaw(std::span<const std::uint8_t> 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<ip_address_t> 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<ip_address_t> 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<StreamResult<std::unique_ptr<Socket>>> c_waitForConnection() noexcept override;
|
|
};
|
|
}
|
|
|
|
#endif // !defined(MIJIN_NET_SOCKET_HPP_INCLUDED)
|