mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-18 19:23:56 +00:00
Add TCP socket support (#74)
This commit is contained in:
@@ -38,7 +38,7 @@ namespace network
|
||||
this->address_.sa_family = AF_UNSPEC;
|
||||
}
|
||||
|
||||
address::address(const std::string& addr, const std::optional<int>& family)
|
||||
address::address(const std::string_view addr, const std::optional<int>& family)
|
||||
: address()
|
||||
{
|
||||
this->parse(addr, family);
|
||||
@@ -286,7 +286,7 @@ namespace network
|
||||
return is_ipv4() || is_ipv6();
|
||||
}
|
||||
|
||||
void address::parse(std::string addr, const std::optional<int>& family)
|
||||
void address::parse(std::string_view addr, const std::optional<int>& family)
|
||||
{
|
||||
std::optional<uint16_t> port_value{};
|
||||
|
||||
@@ -298,7 +298,7 @@ namespace network
|
||||
addr = addr.substr(0, pos);
|
||||
}
|
||||
|
||||
this->resolve(addr, family);
|
||||
this->resolve(std::string(addr), family);
|
||||
|
||||
if (port_value)
|
||||
{
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
@@ -42,7 +43,7 @@ namespace network
|
||||
{
|
||||
public:
|
||||
address();
|
||||
address(const std::string& addr, const std::optional<int>& family = {});
|
||||
address(std::string_view addr, const std::optional<int>& family = std::nullopt);
|
||||
address(const sockaddr_in& addr);
|
||||
address(const sockaddr_in6& addr);
|
||||
address(const sockaddr* addr, socklen_t length);
|
||||
@@ -91,7 +92,7 @@ namespace network
|
||||
sockaddr_storage storage_;
|
||||
};
|
||||
|
||||
void parse(std::string addr, const std::optional<int>& family = {});
|
||||
void parse(std::string_view addr, const std::optional<int>& family = {});
|
||||
void resolve(const std::string& hostname, const std::optional<int>& family = {});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "socket.hpp"
|
||||
#include "address.hpp"
|
||||
|
||||
#include <thread>
|
||||
|
||||
@@ -6,11 +7,15 @@ using namespace std::literals;
|
||||
|
||||
namespace network
|
||||
{
|
||||
socket::socket(const int af)
|
||||
: address_family_(af)
|
||||
socket::socket(SOCKET s)
|
||||
: socket_(s)
|
||||
{
|
||||
}
|
||||
|
||||
socket::socket(const int af, const int type, const int protocol)
|
||||
{
|
||||
initialize_wsa();
|
||||
this->socket_ = ::socket(af, SOCK_DGRAM, IPPROTO_UDP);
|
||||
this->socket_ = ::socket(af, type, protocol);
|
||||
|
||||
if (af == AF_INET6)
|
||||
{
|
||||
@@ -36,16 +41,18 @@ namespace network
|
||||
{
|
||||
this->release();
|
||||
this->socket_ = obj.socket_;
|
||||
this->port_ = obj.port_;
|
||||
this->address_family_ = obj.address_family_;
|
||||
|
||||
obj.socket_ = INVALID_SOCKET;
|
||||
obj.address_family_ = AF_UNSPEC;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
socket::operator bool() const
|
||||
{
|
||||
return this->socket_ != INVALID_SOCKET;
|
||||
}
|
||||
|
||||
void socket::release()
|
||||
{
|
||||
if (this->socket_ != INVALID_SOCKET)
|
||||
@@ -55,43 +62,9 @@ namespace network
|
||||
}
|
||||
}
|
||||
|
||||
bool socket::bind_port(const address& target)
|
||||
bool socket::bind(const address& target)
|
||||
{
|
||||
const auto result = bind(this->socket_, &target.get_addr(), target.get_size()) == 0;
|
||||
if (result)
|
||||
{
|
||||
this->port_ = target.get_port();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool socket::send(const address& target, const void* data, const size_t size) const
|
||||
{
|
||||
const auto res = sendto(this->socket_, static_cast<const char*>(data), static_cast<send_size>(size), 0,
|
||||
&target.get_addr(), target.get_size());
|
||||
return static_cast<size_t>(res) == size;
|
||||
}
|
||||
|
||||
bool socket::send(const address& target, const std::string& data) const
|
||||
{
|
||||
return this->send(target, data.data(), data.size());
|
||||
}
|
||||
|
||||
bool socket::receive(address& source, std::string& data) const
|
||||
{
|
||||
char buffer[0x2000];
|
||||
auto len = source.get_max_size();
|
||||
|
||||
const auto result =
|
||||
recvfrom(this->socket_, buffer, static_cast<int>(sizeof(buffer)), 0, &source.get_addr(), &len);
|
||||
if (result == SOCKET_ERROR)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
data.assign(buffer, buffer + result);
|
||||
return true;
|
||||
return ::bind(this->socket_, &target.get_addr(), target.get_size()) == 0;
|
||||
}
|
||||
|
||||
bool socket::set_blocking(const bool blocking)
|
||||
@@ -156,14 +129,38 @@ namespace network
|
||||
return this->socket_;
|
||||
}
|
||||
|
||||
std::optional<address> socket::get_name() const
|
||||
{
|
||||
address a{};
|
||||
auto len = a.get_max_size();
|
||||
if (getsockname(this->socket_, &a.get_addr(), &len) == SOCKET_ERROR)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
uint16_t socket::get_port() const
|
||||
{
|
||||
return this->port_;
|
||||
const auto address = this->get_name();
|
||||
if (!address)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return address->get_port();
|
||||
}
|
||||
|
||||
int socket::get_address_family() const
|
||||
{
|
||||
return this->address_family_;
|
||||
const auto address = this->get_name();
|
||||
if (!address)
|
||||
{
|
||||
return AF_UNSPEC;
|
||||
}
|
||||
|
||||
return address->get_addr().sa_family;
|
||||
}
|
||||
|
||||
bool socket::sleep_sockets(const std::span<const socket*>& sockets, const std::chrono::milliseconds timeout)
|
||||
|
||||
@@ -4,12 +4,14 @@
|
||||
|
||||
#include <span>
|
||||
#include <chrono>
|
||||
#include <optional>
|
||||
|
||||
#ifdef _WIN32
|
||||
using send_size = int;
|
||||
#define GET_SOCKET_ERROR() (WSAGetLastError())
|
||||
#define poll WSAPoll
|
||||
#define SOCK_WOULDBLOCK WSAEWOULDBLOCK
|
||||
#define SHUT_RDWR SD_BOTH
|
||||
#else
|
||||
using SOCKET = int;
|
||||
using send_size = size_t;
|
||||
@@ -27,8 +29,10 @@ namespace network
|
||||
public:
|
||||
socket() = default;
|
||||
|
||||
socket(int af);
|
||||
~socket();
|
||||
socket(SOCKET s);
|
||||
|
||||
socket(int af, int type, int protocol);
|
||||
virtual ~socket();
|
||||
|
||||
socket(const socket& obj) = delete;
|
||||
socket& operator=(const socket& obj) = delete;
|
||||
@@ -36,11 +40,9 @@ namespace network
|
||||
socket(socket&& obj) noexcept;
|
||||
socket& operator=(socket&& obj) noexcept;
|
||||
|
||||
bool bind_port(const address& target);
|
||||
operator bool() const;
|
||||
|
||||
[[maybe_unused]] bool send(const address& target, const void* data, size_t size) const;
|
||||
[[maybe_unused]] bool send(const address& target, const std::string& data) const;
|
||||
bool receive(address& source, std::string& data) const;
|
||||
bool bind(const address& target);
|
||||
|
||||
bool set_blocking(bool blocking);
|
||||
static bool set_blocking(SOCKET s, bool blocking);
|
||||
@@ -51,6 +53,7 @@ namespace network
|
||||
|
||||
SOCKET get_socket() const;
|
||||
uint16_t get_port() const;
|
||||
std::optional<address> get_name() const;
|
||||
|
||||
int get_address_family() const;
|
||||
|
||||
@@ -61,8 +64,6 @@ namespace network
|
||||
static bool is_socket_ready(SOCKET s, bool in_poll);
|
||||
|
||||
private:
|
||||
int address_family_{AF_UNSPEC};
|
||||
uint16_t port_ = 0;
|
||||
SOCKET socket_ = INVALID_SOCKET;
|
||||
|
||||
void release();
|
||||
|
||||
74
src/common/network/tcp_client_socket.cpp
Normal file
74
src/common/network/tcp_client_socket.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#include "tcp_client_socket.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace network
|
||||
{
|
||||
tcp_client_socket::tcp_client_socket(const int af)
|
||||
: socket(af, SOCK_STREAM, IPPROTO_TCP)
|
||||
{
|
||||
}
|
||||
|
||||
tcp_client_socket::tcp_client_socket(SOCKET s, const address& target)
|
||||
: socket(s)
|
||||
{
|
||||
(void)target;
|
||||
assert(this->get_target() == target);
|
||||
}
|
||||
|
||||
tcp_client_socket::~tcp_client_socket()
|
||||
{
|
||||
if (*this && this->get_target())
|
||||
{
|
||||
::shutdown(this->get_socket(), SHUT_RDWR);
|
||||
}
|
||||
}
|
||||
|
||||
bool tcp_client_socket::send(const void* data, const size_t size) const
|
||||
{
|
||||
const auto res = ::send(this->get_socket(), static_cast<const char*>(data), static_cast<send_size>(size), 0);
|
||||
return static_cast<size_t>(res) == size;
|
||||
}
|
||||
|
||||
bool tcp_client_socket::send(const std::string_view data) const
|
||||
{
|
||||
return this->send(data.data(), data.size());
|
||||
}
|
||||
|
||||
bool tcp_client_socket::receive(std::string& data) const
|
||||
{
|
||||
char buffer[0x2000];
|
||||
|
||||
const auto result = recv(this->get_socket(), buffer, static_cast<int>(sizeof(buffer)), 0);
|
||||
if (result == SOCKET_ERROR)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
data.assign(buffer, buffer + result);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<address> tcp_client_socket::get_target() const
|
||||
{
|
||||
address a{};
|
||||
auto len = a.get_max_size();
|
||||
if (getpeername(this->get_socket(), &a.get_addr(), &len) == SOCKET_ERROR)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
bool tcp_client_socket::connect(const address& target)
|
||||
{
|
||||
if (::connect(this->get_socket(), &target.get_addr(), target.get_size()) != SOCKET_ERROR)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto error = GET_SOCKET_ERROR();
|
||||
return error == SOCK_WOULDBLOCK;
|
||||
}
|
||||
}
|
||||
34
src/common/network/tcp_client_socket.hpp
Normal file
34
src/common/network/tcp_client_socket.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include "socket.hpp"
|
||||
|
||||
#include <string_view>
|
||||
|
||||
namespace network
|
||||
{
|
||||
class tcp_server_socket;
|
||||
|
||||
class tcp_client_socket : public socket
|
||||
{
|
||||
public:
|
||||
tcp_client_socket(int af);
|
||||
|
||||
tcp_client_socket() = default;
|
||||
~tcp_client_socket() override;
|
||||
|
||||
tcp_client_socket(tcp_client_socket&& obj) noexcept = default;
|
||||
tcp_client_socket& operator=(tcp_client_socket&& obj) noexcept = default;
|
||||
|
||||
[[maybe_unused]] bool send(const void* data, size_t size) const;
|
||||
[[maybe_unused]] bool send(std::string_view data) const;
|
||||
bool receive(std::string& data) const;
|
||||
|
||||
std::optional<address> get_target() const;
|
||||
|
||||
bool connect(const address& target);
|
||||
|
||||
private:
|
||||
friend tcp_server_socket;
|
||||
tcp_client_socket(SOCKET s, const address& target);
|
||||
};
|
||||
}
|
||||
34
src/common/network/tcp_server_socket.cpp
Normal file
34
src/common/network/tcp_server_socket.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#include "tcp_server_socket.hpp"
|
||||
|
||||
namespace network
|
||||
{
|
||||
tcp_server_socket::tcp_server_socket(const int af)
|
||||
: socket(af, SOCK_STREAM, IPPROTO_TCP)
|
||||
{
|
||||
}
|
||||
|
||||
tcp_client_socket tcp_server_socket::accept()
|
||||
{
|
||||
this->listen();
|
||||
|
||||
address a{};
|
||||
auto len = a.get_max_size();
|
||||
const auto s = ::accept(this->get_socket(), &a.get_addr(), &len);
|
||||
if (s == INVALID_SOCKET)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return {s, a};
|
||||
}
|
||||
|
||||
void tcp_server_socket::listen()
|
||||
{
|
||||
if (this->listening_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this->listening_ = ::listen(this->get_socket(), 32) == 0;
|
||||
}
|
||||
}
|
||||
26
src/common/network/tcp_server_socket.hpp
Normal file
26
src/common/network/tcp_server_socket.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "socket.hpp"
|
||||
#include "tcp_client_socket.hpp"
|
||||
|
||||
namespace network
|
||||
{
|
||||
class tcp_server_socket : public socket
|
||||
{
|
||||
public:
|
||||
tcp_server_socket(int af);
|
||||
|
||||
tcp_server_socket() = default;
|
||||
~tcp_server_socket() override = default;
|
||||
|
||||
tcp_server_socket(tcp_server_socket&& obj) noexcept = default;
|
||||
tcp_server_socket& operator=(tcp_server_socket&& obj) noexcept = default;
|
||||
|
||||
tcp_client_socket accept();
|
||||
|
||||
private:
|
||||
bool listening_{false};
|
||||
|
||||
void listen();
|
||||
};
|
||||
}
|
||||
37
src/common/network/udp_socket.cpp
Normal file
37
src/common/network/udp_socket.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#include "udp_socket.hpp"
|
||||
|
||||
namespace network
|
||||
{
|
||||
udp_socket::udp_socket(const int af)
|
||||
: socket(af, SOCK_DGRAM, IPPROTO_UDP)
|
||||
{
|
||||
}
|
||||
|
||||
bool udp_socket::send(const address& target, const void* data, const size_t size) const
|
||||
{
|
||||
const auto res = sendto(this->get_socket(), static_cast<const char*>(data), static_cast<send_size>(size), 0,
|
||||
&target.get_addr(), target.get_size());
|
||||
return static_cast<size_t>(res) == size;
|
||||
}
|
||||
|
||||
bool udp_socket::send(const address& target, const std::string_view data) const
|
||||
{
|
||||
return this->send(target, data.data(), data.size());
|
||||
}
|
||||
|
||||
bool udp_socket::receive(address& source, std::string& data) const
|
||||
{
|
||||
char buffer[0x2000];
|
||||
auto len = source.get_max_size();
|
||||
|
||||
const auto result =
|
||||
recvfrom(this->get_socket(), buffer, static_cast<int>(sizeof(buffer)), 0, &source.get_addr(), &len);
|
||||
if (result == SOCKET_ERROR)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
data.assign(buffer, buffer + result);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
22
src/common/network/udp_socket.hpp
Normal file
22
src/common/network/udp_socket.hpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "socket.hpp"
|
||||
|
||||
#include <string_view>
|
||||
|
||||
namespace network
|
||||
{
|
||||
struct udp_socket : socket
|
||||
{
|
||||
udp_socket(int af);
|
||||
udp_socket() = default;
|
||||
~udp_socket() override = default;
|
||||
|
||||
udp_socket(udp_socket&& obj) noexcept = default;
|
||||
udp_socket& operator=(udp_socket&& obj) noexcept = default;
|
||||
|
||||
[[maybe_unused]] bool send(const address& target, const void* data, size_t size) const;
|
||||
[[maybe_unused]] bool send(const address& target, std::string_view data) const;
|
||||
bool receive(address& source, std::string& data) const;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user