From a9f963b54596a97bdbf8dd83c95c39970aff8b68 Mon Sep 17 00:00:00 2001 From: Tyler Wilding Date: Sun, 1 May 2022 16:15:26 -0400 Subject: [PATCH] game: pull out single-client logic from XSocketServer --- common/cross_sockets/XSocketServer.cpp | 38 +---------------- common/cross_sockets/XSocketServer.h | 18 +++----- game/runtime.cpp | 2 +- game/system/Deci2Server.cpp | 57 +++++++++++++++++++++++--- game/system/Deci2Server.h | 16 +++++++- test/test_listener_deci2.cpp | 44 ++++++++++---------- 6 files changed, 97 insertions(+), 78 deletions(-) diff --git a/common/cross_sockets/XSocketServer.cpp b/common/cross_sockets/XSocketServer.cpp index 0e7dbe92a2..197ea646d6 100644 --- a/common/cross_sockets/XSocketServer.cpp +++ b/common/cross_sockets/XSocketServer.cpp @@ -25,16 +25,8 @@ XSocketServer::~XSocketServer() { } void XSocketServer::shutdown_server() { - // Cleanup the accept thread - if (accept_thread_running) { - kill_accept_thread = true; - accept_thread.join(); - accept_thread_running = false; - } - // Close the listening and accepted socket socket close_server_socket(); - close_socket(accepted_socket); } bool XSocketServer::init_server() { @@ -83,8 +75,8 @@ bool XSocketServer::init_server() { } server_initialized = true; - accept_thread = std::thread(&XSocketServer::accept_thread_func, this); - fmt::print("[XSocketServer:{}] awaiting connections\n", tcp_port); + fmt::print("[XSocketServer:{}] initialized\n", tcp_port); + post_init(); return true; } @@ -92,29 +84,3 @@ void XSocketServer::close_server_socket() { close_socket(listening_socket); listening_socket = -1; } - -void XSocketServer::accept_thread_func() { - socklen_t l = sizeof(addr); - while (!kill_accept_thread) { - if (accepted_socket == -1) { - this->accepted_socket = accept_socket(listening_socket, (sockaddr*)&addr, &l); - fmt::print("Accept Socket in XSocketServer: {}\n", this->accepted_socket); - set_socket_timeout(this->accepted_socket, 100000); - write_on_accept(); - client_connected = true; - } - std::this_thread::sleep_for(std::chrono::microseconds(50000)); - } -} - -bool XSocketServer::wait_for_connection() { - return client_connected; -} - -void XSocketServer::lock() { - server_mutex.lock(); -} - -void XSocketServer::unlock() { - server_mutex.unlock(); -} diff --git a/common/cross_sockets/XSocketServer.h b/common/cross_sockets/XSocketServer.h index 6177993079..f1acbc9827 100644 --- a/common/cross_sockets/XSocketServer.h +++ b/common/cross_sockets/XSocketServer.h @@ -11,28 +11,25 @@ class XSocketServer { public: static constexpr int DEF_BUFFER_SIZE = 32 * 1024 * 1024; - XSocketServer(const XSocketServer&) = delete; - XSocketServer& operator=(const XSocketServer&) = delete; XSocketServer(std::function shutdown_callback, int _tcp_port, int _buffer_size = DEF_BUFFER_SIZE); virtual ~XSocketServer(); + + XSocketServer(const XSocketServer&) = delete; + XSocketServer& operator=(const XSocketServer&) = delete; + bool init_server(); void shutdown_server(); void close_server_socket(); - bool wait_for_connection(); - void lock(); - void unlock(); - // Abstract methods -- use-case dependent - virtual void write_on_accept() = 0; + virtual void post_init() = 0; protected: int tcp_port; struct sockaddr_in addr = {}; int listening_socket = -1; - int accepted_socket = -1; std::vector buffer; bool kill_accept_thread = false; @@ -41,9 +38,4 @@ class XSocketServer { bool client_connected = false; std::function want_exit_callback; - std::thread accept_thread; - - std::mutex server_mutex; - - void accept_thread_func(); }; diff --git a/game/runtime.cpp b/game/runtime.cpp index 27514391d9..5cc019f86e 100644 --- a/game/runtime.cpp +++ b/game/runtime.cpp @@ -93,7 +93,7 @@ void deci2_runner(SystemThreadInterface& iface) { lg::debug("[DECI2] Waiting for listener..."); bool saw_listener = false; while (!iface.get_want_exit()) { - if (server.wait_for_connection()) { + if (server.is_client_connected()) { if (!saw_listener) { lg::debug("[DECI2] Connected!"); } diff --git a/game/system/Deci2Server.cpp b/game/system/Deci2Server.cpp index bfbbab03a2..6951dc8443 100644 --- a/game/system/Deci2Server.cpp +++ b/game/system/Deci2Server.cpp @@ -14,11 +14,46 @@ #include "third-party/fmt/core.h" -void Deci2Server::write_on_accept() { - u32 versions[2] = {versions::GOAL_VERSION_MAJOR, versions::GOAL_VERSION_MINOR}; - lock(); - write_to_socket(accepted_socket, (char*)&versions, 8); - unlock(); +#ifdef _WIN32 +#define NOMINMAX +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#endif + +Deci2Server::~Deci2Server() { + // Cleanup the accept thread + if (accept_thread_running) { + kill_accept_thread = true; + accept_thread.join(); + accept_thread_running = false; + } + close_socket(accepted_socket); +} + +void Deci2Server::post_init() { + fmt::print("[Deci2Server:{}] awaiting connections\n", tcp_port); + accept_thread = std::thread(&Deci2Server::accept_thread_func, this); +} + +void Deci2Server::accept_thread_func() { + socklen_t addr_len = sizeof(addr); + while (!kill_accept_thread) { + if (accepted_socket == -1) { + accepted_socket = accept_socket(listening_socket, (sockaddr*)&addr, &addr_len); + set_socket_timeout(accepted_socket, 100000); + u32 versions[2] = {versions::GOAL_VERSION_MAJOR, versions::GOAL_VERSION_MINOR}; + write_to_socket(accepted_socket, (char*)&versions, 8); + client_connected = true; + return; // stop accepting connections + } + std::this_thread::sleep_for(std::chrono::microseconds(50000)); + } +} + +bool Deci2Server::is_client_connected() { + return client_connected; } /*! @@ -48,6 +83,10 @@ void Deci2Server::send_proto_ready(Deci2Driver* drivers, int* driver_count) { } void Deci2Server::read_data() { + if (!is_client_connected()) { + return; + } + int desired_size = (int)sizeof(Deci2Header); int got = 0; @@ -133,3 +172,11 @@ void Deci2Server::send_data(void* buf, u16 len) { } unlock(); } + +void Deci2Server::lock() { + server_mutex.lock(); +} + +void Deci2Server::unlock() { + server_mutex.unlock(); +} diff --git a/game/system/Deci2Server.h b/game/system/Deci2Server.h index 67a14adf8c..6cc8fd4539 100644 --- a/game/system/Deci2Server.h +++ b/game/system/Deci2Server.h @@ -10,17 +10,31 @@ class Deci2Server : public XSocketServer { public: using XSocketServer::XSocketServer; + virtual ~Deci2Server(); + + void post_init() override; + void pre_shutdown() override; - void write_on_accept() override; void read_data(); void send_data(void* buf, u16 len); + bool is_client_connected(); void wait_for_protos_ready(); void send_proto_ready(Deci2Driver* drivers, int* driver_count); + void lock(); + void unlock(); + private: bool protocols_ready = false; std::condition_variable cv; Deci2Driver* d2_drivers = nullptr; int* d2_driver_count = nullptr; + + int accepted_socket = -1; + + std::thread accept_thread; + std::mutex server_mutex; + + void accept_thread_func(); }; diff --git a/test/test_listener_deci2.cpp b/test/test_listener_deci2.cpp index 45cd828496..cf7e706be8 100644 --- a/test/test_listener_deci2.cpp +++ b/test/test_listener_deci2.cpp @@ -40,26 +40,26 @@ TEST(Listener, ListenToNothing) { TEST(Listener, DeciCheckNoListener) { Deci2Server s(always_false, DECI2_PORT); EXPECT_TRUE(s.init_server()); - EXPECT_FALSE(s.wait_for_connection()); - EXPECT_FALSE(s.wait_for_connection()); - EXPECT_FALSE(s.wait_for_connection()); + EXPECT_FALSE(s.is_client_connected()); + EXPECT_FALSE(s.is_client_connected()); + EXPECT_FALSE(s.is_client_connected()); } TEST(Listener, CheckConnectionStaysAlive) { Deci2Server s(always_false, DECI2_PORT); EXPECT_TRUE(s.init_server()); - EXPECT_FALSE(s.wait_for_connection()); + EXPECT_FALSE(s.is_client_connected()); Listener l; - EXPECT_FALSE(s.wait_for_connection()); + EXPECT_FALSE(s.is_client_connected()); bool connected = l.connect_to_target(); EXPECT_TRUE(connected); // TODO - some sort of backoff and retry would be better - while (connected && !s.wait_for_connection()) { + while (connected && !s.is_client_connected()) { } - EXPECT_TRUE(s.wait_for_connection()); + EXPECT_TRUE(s.is_client_connected()); std::this_thread::sleep_for(std::chrono::milliseconds(500)); - EXPECT_TRUE(s.wait_for_connection()); + EXPECT_TRUE(s.is_client_connected()); EXPECT_TRUE(l.is_connected()); } @@ -67,19 +67,19 @@ TEST(Listener, DeciThenListener) { for (int i = 0; i < 3; i++) { Deci2Server s(always_false, DECI2_PORT); EXPECT_TRUE(s.init_server()); - EXPECT_FALSE(s.wait_for_connection()); - EXPECT_FALSE(s.wait_for_connection()); - EXPECT_FALSE(s.wait_for_connection()); + EXPECT_FALSE(s.is_client_connected()); + EXPECT_FALSE(s.is_client_connected()); + EXPECT_FALSE(s.is_client_connected()); Listener l; - EXPECT_FALSE(s.wait_for_connection()); - EXPECT_FALSE(s.wait_for_connection()); + EXPECT_FALSE(s.is_client_connected()); + EXPECT_FALSE(s.is_client_connected()); bool connected = l.connect_to_target(); EXPECT_TRUE(connected); // TODO - some sort of backoff and retry would be better - while (connected && !s.wait_for_connection()) { + while (connected && !s.is_client_connected()) { } - EXPECT_TRUE(s.wait_for_connection()); + EXPECT_TRUE(s.is_client_connected()); } } @@ -87,12 +87,12 @@ TEST(Listener, DeciThenListener2) { for (int i = 0; i < 3; i++) { Deci2Server s(always_false, DECI2_PORT); EXPECT_TRUE(s.init_server()); - EXPECT_FALSE(s.wait_for_connection()); - EXPECT_FALSE(s.wait_for_connection()); - EXPECT_FALSE(s.wait_for_connection()); + EXPECT_FALSE(s.is_client_connected()); + EXPECT_FALSE(s.is_client_connected()); + EXPECT_FALSE(s.is_client_connected()); Listener l; - EXPECT_FALSE(s.wait_for_connection()); - EXPECT_FALSE(s.wait_for_connection()); + EXPECT_FALSE(s.is_client_connected()); + EXPECT_FALSE(s.is_client_connected()); EXPECT_TRUE(l.connect_to_target()); } } @@ -103,11 +103,11 @@ TEST(Listener, ListenerThenDeci) { EXPECT_FALSE(l.connect_to_target()); Deci2Server s(always_false, DECI2_PORT); EXPECT_TRUE(s.init_server()); - EXPECT_FALSE(s.wait_for_connection()); + EXPECT_FALSE(s.is_client_connected()); bool connected = l.connect_to_target(); EXPECT_TRUE(connected); // TODO - some sort of backoff and retry would be better - while (connected && !s.wait_for_connection()) { + while (connected && !s.is_client_connected()) { } } }