implemented socket_TCP_enableAliveChecks

This commit is contained in:
Timerix 2025-11-09 00:49:01 +05:00
parent ebab072835
commit 13ccfc7ff9
5 changed files with 78 additions and 35 deletions

View File

@ -13,6 +13,7 @@
// include OS-dependent socket headers // include OS-dependent socket headers
#if KN_USE_WINSOCK #if KN_USE_WINSOCK
#include <winsock2.h> #include <winsock2.h>
#include <ws2ipdef.h>
// There you can see what error codes mean. // There you can see what error codes mean.
#include <winerror.h> #include <winerror.h>
#else #else

View File

@ -13,13 +13,9 @@ Result(void) network_init(){
return RESULT_VOID; return RESULT_VOID;
} }
Result(void) network_deinit(){ void network_deinit(){
#if _WIN32 #if _WIN32
// Deinitialize Winsock // Deinitialize Winsock
int result = WSACleanup(); (void)WSACleanup();
if (result != 0) {
return RESULT_ERROR_FMT("WSACleanup failed with error code 0x%X", result);
}
#endif #endif
return RESULT_VOID;
} }

View File

@ -2,4 +2,4 @@
#include "tlibc/errors.h" #include "tlibc/errors.h"
Result(void) network_init(); Result(void) network_init();
Result(void) network_deinit(); void network_deinit();

View File

@ -3,7 +3,7 @@
#include <assert.h> #include <assert.h>
Result(Socket) socket_open_TCP(){ 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){ if(s == -1){
return RESULT_ERROR_SOCKET(); return RESULT_ERROR_SOCKET();
} }
@ -25,29 +25,6 @@ Result(void) socket_shutdown(Socket s, SocketShutdownType direction){
return RESULT_VOID; 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){ Result(void) socket_bind(Socket s, EndpointIPv4 local_end){
struct sockaddr_in sockaddr = EndpointIPv4_toSockaddr(local_end); struct sockaddr_in sockaddr = EndpointIPv4_toSockaddr(local_end);
if(bind(s, (void*)&sockaddr, sizeof(sockaddr)) != 0) 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); *remote_end = EndpointIPv4_fromSockaddr(remote_addr);
return RESULT_VALUE(i, r); 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;
}

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "endpoint.h" #include "endpoint.h"
#include "tlibc/errors.h" #include "tlibc/errors.h"
#include "tlibc/time.h"
#include "tlibc/collections/Array.h" #include "tlibc/collections/Array.h"
typedef enum SocketShutdownType { typedef enum SocketShutdownType {
@ -17,13 +18,9 @@ typedef enum SocketRecvFlag {
typedef i64 Socket; typedef i64 Socket;
#define SOCKET_TIMEOUT_MS_DEFAULT 5000
#define SOCKET_TIMEOUT_MS_INFINITE 0
Result(Socket) socket_open_TCP(); Result(Socket) socket_open_TCP();
void socket_close(Socket s); void socket_close(Socket s);
Result(void) socket_shutdown(Socket s, SocketShutdownType direction); 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_bind(Socket s, EndpointIPv4 local_end);
Result(void) socket_listen(Socket s, i32 backlog); 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(void) socket_sendto(Socket s, Array(u8) buffer, EndpointIPv4 dst);
Result(i32) socket_recv(Socket s, Array(u8) buffer, SocketRecvFlag flags); 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); 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);