From 8d71146b819093eb66eea697dd8d8453fa01a6c5 Mon Sep 17 00:00:00 2001 From: Tyler Wilding Date: Sat, 21 May 2022 15:49:01 -0400 Subject: [PATCH] game/deci2: Add a timeout on waiting for a client to connect to DECI2 (#1367) game/deci2: Add a timeout to waiting for a client to connect --- common/cross_sockets/XSocket.cpp | 37 ++++++++++++++++++++++++++++++++ common/cross_sockets/XSocket.h | 2 ++ game/system/Deci2Server.cpp | 5 ++++- 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/common/cross_sockets/XSocket.cpp b/common/cross_sockets/XSocket.cpp index a65f7077e9..4c60c41a06 100644 --- a/common/cross_sockets/XSocket.cpp +++ b/common/cross_sockets/XSocket.cpp @@ -38,6 +38,20 @@ int open_socket(int af, int type, int protocol) { int accept_socket(int socket, sockaddr* addr, socklen_t* addrLen) { return accept(socket, addr, addrLen); } +int select_and_accept_socket(int socket, sockaddr* addr, socklen_t* addrLen, int microSeconds) { + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = microSeconds; + // Use select so it can timeout, accept on the returned socket if it is correct + fd_set read_sockets; + FD_ZERO(&read_sockets); + FD_SET(socket, &read_sockets); + auto activity = select(socket + 1, &read_sockets, NULL, NULL, &timeout); + if (activity > 0) { + return accept(socket, addr, addrLen); + } + return -1; +} #endif #ifdef _WIN32 @@ -52,6 +66,29 @@ int accept_socket(int socket, sockaddr* addr, int* addrLen) { } return accept(socket, addr, addrLen); } + +int select_and_accept_socket(int socket, sockaddr* addr, int* addrLen, int microSeconds) { + WSADATA wsaData = {0}; + int iResult = 0; + // Initialize Winsock + iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (iResult != 0) { + printf("WSAStartup failed: %d\n", iResult); + return 1; + } + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = microSeconds; + // Use select so it can timeout, accept on the returned socket if it is correct + fd_set read_sockets; + FD_ZERO(&read_sockets); + FD_SET(socket, &read_sockets); + auto activity = select(socket + 1, &read_sockets, NULL, NULL, &timeout); + if (activity > 0) { + return accept(socket, addr, addrLen); + } + return -1; +} #endif void close_socket(int sock) { diff --git a/common/cross_sockets/XSocket.h b/common/cross_sockets/XSocket.h index 680f80b923..d73306b518 100644 --- a/common/cross_sockets/XSocket.h +++ b/common/cross_sockets/XSocket.h @@ -27,8 +27,10 @@ const int TCP_SOCKET_LEVEL = IPPROTO_TCP; int open_socket(int af, int type, int protocol); #ifdef __linux int accept_socket(int socket, sockaddr* addr, socklen_t* addrLen); +int select_and_accept_socket(int socket, sockaddr* addr, socklen_t* addrLen, int microSeconds); #elif _WIN32 int accept_socket(int socket, sockaddr* addr, int* addrLen); +int select_and_accept_socket(int socket, sockaddr* addr, int* addrLen, int microSeconds); #endif void close_socket(int sock); int set_socket_option(int socket, int level, int optname, const void* optval, int optlen); diff --git a/game/system/Deci2Server.cpp b/game/system/Deci2Server.cpp index 338f64c111..7eae6cefc6 100644 --- a/game/system/Deci2Server.cpp +++ b/game/system/Deci2Server.cpp @@ -26,6 +26,8 @@ Deci2Server::~Deci2Server() { // Cleanup the accept thread if (accept_thread_running) { kill_accept_thread = true; + // NOTE - if we don't want to wait for the roundtrip timeout to exit the game gracefully + // we should just terminate the thread forcefully accept_thread.join(); accept_thread_running = false; } @@ -43,7 +45,8 @@ void Deci2Server::post_init() { void Deci2Server::accept_thread_func() { socklen_t addr_len = sizeof(addr); while (!kill_accept_thread) { - accepted_socket = accept_socket(listening_socket, (sockaddr*)&addr, &addr_len); + accepted_socket = select_and_accept_socket(listening_socket, (sockaddr*)&addr, &addr_len, + 100000); // 0.1 second timeout if (accepted_socket >= 0) { set_socket_timeout(accepted_socket, 100000); u32 versions[2] = {versions::GOAL_VERSION_MAJOR, versions::GOAL_VERSION_MINOR};