From 13ccfc7ff992b84b6f34efb84e9709f294033ac3 Mon Sep 17 00:00:00 2001 From: Timerix Date: Sun, 9 Nov 2025 00:49:01 +0500 Subject: [PATCH] implemented socket_TCP_enableAliveChecks --- src/network/internal.h | 1 + src/network/network.c | 8 ++--- src/network/network.h | 2 +- src/network/socket.c | 82 +++++++++++++++++++++++++++++------------- src/network/socket.h | 20 ++++++++--- 5 files changed, 78 insertions(+), 35 deletions(-) diff --git a/src/network/internal.h b/src/network/internal.h index f2feea1..79f3076 100644 --- a/src/network/internal.h +++ b/src/network/internal.h @@ -13,6 +13,7 @@ // include OS-dependent socket headers #if KN_USE_WINSOCK #include + #include // There you can see what error codes mean. #include #else diff --git a/src/network/network.c b/src/network/network.c index 3e97488..fb129b8 100755 --- a/src/network/network.c +++ b/src/network/network.c @@ -13,13 +13,9 @@ Result(void) network_init(){ return RESULT_VOID; } -Result(void) network_deinit(){ +void network_deinit(){ #if _WIN32 // Deinitialize Winsock - int result = WSACleanup(); - if (result != 0) { - return RESULT_ERROR_FMT("WSACleanup failed with error code 0x%X", result); - } + (void)WSACleanup(); #endif - return RESULT_VOID; } diff --git a/src/network/network.h b/src/network/network.h index 7134aaf..12f080c 100755 --- a/src/network/network.h +++ b/src/network/network.h @@ -2,4 +2,4 @@ #include "tlibc/errors.h" Result(void) network_init(); -Result(void) network_deinit(); +void network_deinit(); diff --git a/src/network/socket.c b/src/network/socket.c index c0546b3..7f2e859 100755 --- a/src/network/socket.c +++ b/src/network/socket.c @@ -3,7 +3,7 @@ #include Result(Socket) socket_open_TCP(){ - Socket s = socket(AF_INET, SOCK_STREAM, 0); + Socket s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(s == -1){ return RESULT_ERROR_SOCKET(); } @@ -25,29 +25,6 @@ Result(void) socket_shutdown(Socket s, SocketShutdownType direction){ return RESULT_VOID; } -Result(void) socket_setTimeout(Socket s, u32 ms){ - void* opt; - u32 optlen; - -#if KN_USE_WINSOCK - opt = &ms; - optlen = sizeof(ms); -#else - struct timeval tv = { - .tv_sec = ms/1000, - .tv_usec = (ms%1000)*1000 - }; - opt = &tv; - optlen = sizeof(tv); -#endif - - if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, opt, optlen) != 0) - return RESULT_ERROR_SOCKET(); - if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, opt, optlen) != 0) - return RESULT_ERROR_SOCKET(); - return RESULT_VOID; -} - Result(void) socket_bind(Socket s, EndpointIPv4 local_end){ struct sockaddr_in sockaddr = EndpointIPv4_toSockaddr(local_end); if(bind(s, (void*)&sockaddr, sizeof(sockaddr)) != 0) @@ -146,3 +123,60 @@ Result(i32) socket_recvfrom(Socket s, Array(u8) buffer, SocketRecvFlag flags, NU *remote_end = EndpointIPv4_fromSockaddr(remote_addr); return RESULT_VALUE(i, r); } + + + +#define try_setsockopt(socket, level, OPT){ \ + if(setsockopt(socket, level, OPT, (void*)&opt_##OPT, sizeof(opt_##OPT)) != 0)\ + return RESULT_ERROR_SOCKET();\ +} + +Result(void) socket_TCP_enableAliveChecks(Socket s, + sec_t first_check_time, u32 checks_count, sec_t checks_interval) +{ +#if KN_USE_WINSOCK + BOOL opt_SO_KEEPALIVE = 1; // enable keepalives + DWORD opt_TCP_KEEPIDLE = first_check_time; + DWORD opt_TCP_KEEPCNT = checks_count; + DWORD opt_TCP_KEEPINTVL = checks_interval; + try_setsockopt(s, SOL_SOCKET, SO_KEEPALIVE); + try_setsockopt(s, IPPROTO_TCP, TCP_KEEPIDLE); + try_setsockopt(s, IPPROTO_TCP, TCP_KEEPCNT); + try_setsockopt(s, IPPROTO_TCP, TCP_KEEPINTVL); + + // timeout for connect() + DWORD opt_TCP_MAXRT = checks_count * checks_interval; + try_setsockopt(s, IPPROTO_TCP, TCP_MAXRT); +#else + int opt_SO_KEEPALIVE = 1; // enable keepalives + int opt_TCP_KEEPIDLE = first_check_time; + int opt_TCP_KEEPCNT = checks_count; + int opt_TCP_KEEPINTVL = checks_interval; + try_setsockopt(s, SOL_SOCKET, SO_KEEPALIVE); + try_setsockopt(s, IPPROTO_TCP, TCP_KEEPIDLE); + try_setsockopt(s, IPPROTO_TCP, TCP_KEEPCNT); + try_setsockopt(s, IPPROTO_TCP, TCP_KEEPINTVL); + + // read more in the article + int opt_TCP_USER_TIMEOUT = checks_count * checks_interval * 1000; + try_setsockopt(s, IPPROTO_TCP, TCP_USER_TIMEOUT); +#endif + return RESULT_VOID; +} + +Result(void) socket_setTimeout(Socket s, u32 ms){ +#if KN_USE_WINSOCK + DWORD opt_SO_SNDTIMEO = ms; + DWORD opt_SO_RCVTIMEO = opt_SO_SNDTIMEO; +#else + struct timeval opt_SO_SNDTIMEO = { + .tv_sec = ms/1000, + .tv_usec = (ms%1000)*1000 + }; + struct timeval opt_SO_RCVTIMEO = opt_SO_SNDTIMEO; +#endif + + try_setsockopt(s, SOL_SOCKET, SO_SNDTIMEO); + try_setsockopt(s, SOL_SOCKET, SO_RCVTIMEO); + return RESULT_VOID; +} \ No newline at end of file diff --git a/src/network/socket.h b/src/network/socket.h index 7e8dadb..6d6cdfc 100755 --- a/src/network/socket.h +++ b/src/network/socket.h @@ -1,6 +1,7 @@ #pragma once #include "endpoint.h" #include "tlibc/errors.h" +#include "tlibc/time.h" #include "tlibc/collections/Array.h" typedef enum SocketShutdownType { @@ -17,13 +18,9 @@ typedef enum SocketRecvFlag { typedef i64 Socket; -#define SOCKET_TIMEOUT_MS_DEFAULT 5000 -#define SOCKET_TIMEOUT_MS_INFINITE 0 - Result(Socket) socket_open_TCP(); void socket_close(Socket s); Result(void) socket_shutdown(Socket s, SocketShutdownType direction); -Result(void) socket_setTimeout(Socket s, u32 ms); Result(void) socket_bind(Socket s, EndpointIPv4 local_end); Result(void) socket_listen(Socket s, i32 backlog); @@ -34,3 +31,18 @@ Result(void) socket_send(Socket s, Array(u8) buffer); Result(void) socket_sendto(Socket s, Array(u8) buffer, EndpointIPv4 dst); Result(i32) socket_recv(Socket s, Array(u8) buffer, SocketRecvFlag flags); Result(i32) socket_recvfrom(Socket s, Array(u8) buffer, SocketRecvFlag flags, NULLABLE(EndpointIPv4*) remote_end); + +/// Enables sending SO_KEEPALIVE packets when socket is idling. +/// Also enables TCP_USER_TIMEOUT to handle situations +/// when socket is not sending KEEPALIVE packets. +/// Read more: https://blog.cloudflare.com/when-tcp-sockets-refuse-to-die/ +/// RU translaton: https://habr.com/ru/articles/700470/ +Result(void) socket_TCP_enableAliveChecks(Socket s, + sec_t first_check_time, u32 checks_count, sec_t checks_interval); +#define socket_TCP_enableAliveChecks_default(socket) \ + socket_TCP_enableAliveChecks(socket, 1, 4, 5) + +#define SOCKET_TIMEOUT_MS_DEFAULT 5000 +#define SOCKET_TIMEOUT_MS_INFINITE 0 +/// @brief sets general timeout for send() and recv() +Result(void) socket_setTimeout(Socket s, u32 ms);