ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
client.h File Reference

Go to the source code of this file.

Data Structures

struct  tcp_client
 TCP client connection state (network-layer only) More...
 

Typedefs

typedef struct tcp_client tcp_client_t
 TCP client connection state (network-layer only)
 

Functions

tcp_client_t * tcp_client_create (void)
 Create and initialize a TCP client instance.
 
void tcp_client_destroy (tcp_client_t **client_ptr)
 Destroy TCP client and free all resources.
 
bool tcp_client_is_active (const tcp_client_t *client)
 Check if connection is currently active.
 
bool tcp_client_is_lost (const tcp_client_t *client)
 Check if connection was lost.
 
socket_t tcp_client_get_socket (const tcp_client_t *client)
 Get current socket descriptor.
 
uint32_t tcp_client_get_id (const tcp_client_t *client)
 Get client ID assigned by server.
 
void tcp_client_signal_lost (tcp_client_t *client)
 Signal that connection was lost (triggers reconnection)
 
void tcp_client_close (tcp_client_t *client)
 Close connection gracefully.
 
void tcp_client_shutdown (tcp_client_t *client)
 Shutdown connection forcefully (for signal handlers)
 
int tcp_client_connect (tcp_client_t *client, const char *address, int port, int reconnect_attempt, bool first_connection, bool has_ever_connected)
 Establish TCP connection to server.
 
int tcp_client_send_packet (tcp_client_t *client, packet_type_t type, const void *data, size_t len)
 Send packet with thread-safe mutex protection.
 
int tcp_client_send_ping (tcp_client_t *client)
 Send ping packet.
 
int tcp_client_send_pong (tcp_client_t *client)
 Send pong packet.
 

Typedef Documentation

◆ tcp_client_t

typedef struct tcp_client tcp_client_t

TCP client connection state (network-layer only)

Encapsulates network-specific state for a single TCP client connection:

  • Socket file descriptor
  • Server address information
  • Connection state flags
  • Send mutex for thread-safe packet transmission
  • Encryption flag

Application-layer state (audio, threads, display, crypto) is now in app_client_t (network/client.h) to enable transport-agnostic design.

Ownership Model

  • Created by tcp_client_create() in main thread
  • Owned by connection management layer
  • Destroyed after network I/O is complete

Thread Safety

  • Atomic fields: Safe for concurrent read/write without locks
  • Mutex-protected fields: Acquire send_mutex before socket writes
  • Immutable after init: server_ip is set once, then read-only
See also
app_client_t For application-layer state
websocket_client_t For WebSocket transport equivalent

Function Documentation

◆ tcp_client_close()

void tcp_client_close ( tcp_client_t *  client)

Close connection gracefully.

Parameters
clientTCP client instance

Definition at line 216 of file lib/network/tcp/client.c.

216 {
217 if (!client)
218 return;
219
220 log_debug("Closing client connection");
221
222 // Mark connection as inactive
223 atomic_store_bool(&client->connection_active, false);
224
225 // Close socket
226 if (socket_is_valid(client->sockfd)) {
227 close_socket_safe(client->sockfd);
228 client->sockfd = INVALID_SOCKET_VALUE;
229 }
230
231 // Reset client ID
232 client->my_client_id = 0;
233}
void atomic_store_bool(atomic_t *a, bool value)
Atomically store a boolean value.
Definition atomic.c:177
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548
bool socket_is_valid(socket_t sock)
Check if a socket handle is valid.
#define INVALID_SOCKET_VALUE
Invalid socket value (POSIX: -1)
Definition socket.h:278

References atomic_store_bool(), INVALID_SOCKET_VALUE, log_debug, and socket_is_valid().

◆ tcp_client_connect()

int tcp_client_connect ( tcp_client_t *  client,
const char *  address,
int  port,
int  reconnect_attempt,
bool  first_connection,
bool  has_ever_connected 
)

Establish TCP connection to server.

Performs full connection lifecycle including DNS resolution, socket creation, connection with timeout, and socket configuration. Does NOT perform crypto handshake or send initial packets - those are application responsibilities.

Parameters
clientTCP client instance
addressServer hostname or IP address
portServer port number
reconnect_attemptCurrent reconnection attempt (0 for first, 1+ for retries)
first_connectionTrue if this is the very first connection since program start
has_ever_connectedTrue if client has successfully connected at least once
Returns
0 on success, negative on error

Performs full connection lifecycle:

  • DNS resolution with IPv4/IPv6 dual-stack support
  • Socket creation and connection with timeout
  • Crypto handshake (if enabled)
  • Initial capability exchange
  • Client ID assignment from local port
Parameters
clientTCP client instance
addressServer hostname or IP address
portServer port number
reconnect_attemptCurrent reconnection attempt (0 for first, 1+ for retries)
first_connectionTrue if this is the very first connection since program start
has_ever_connectedTrue if client has successfully connected at least once
Returns
0 on success, negative on error

Definition at line 325 of file lib/network/tcp/client.c.

326 {
327 (void)first_connection; // Currently unused
328 (void)has_ever_connected; // Currently unused
329
330 if (!client || !address || port <= 0) {
331 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid client, address, or port");
332 }
333
334 // Close any existing connection
335 if (socket_is_valid(client->sockfd)) {
336 close_socket_safe(client->sockfd);
337 client->sockfd = INVALID_SOCKET_VALUE;
338 }
339
340 // Apply reconnection delay if this is a retry
341 if (reconnect_attempt > 0) {
342 uint64_t delay_ns = get_reconnect_delay(reconnect_attempt);
343 platform_sleep_ns(delay_ns);
344 }
345
346 // Resolve server address using getaddrinfo() for IPv4/IPv6 support
347 // Special handling for localhost: ensure we try both IPv6 (::1) and IPv4 (127.0.0.1)
348 bool is_localhost = (strcmp(address, "localhost") == 0 || is_localhost_ipv4(address) || is_localhost_ipv6(address));
349
350 struct addrinfo hints, *res = NULL, *addr_iter;
351 memset(&hints, 0, sizeof(hints));
352 hints.ai_family = AF_UNSPEC; // Allow IPv4 or IPv6
353 hints.ai_socktype = SOCK_STREAM;
354 if (is_localhost) {
355 hints.ai_flags = AI_NUMERICSERV; // Optimize for localhost
356 }
357
358 char port_str[16];
359 SAFE_SNPRINTF(port_str, sizeof(port_str), "%d", port);
360
361 // For localhost, try IPv6 loopback (::1) first, then fall back to IPv4
362 if (is_localhost) {
363 log_debug("Localhost detected - trying IPv6 loopback [::1]:%s first...", port_str);
364 hints.ai_family = AF_INET6;
365 hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
366
367 int ipv6_result = getaddrinfo("::1", port_str, &hints, &res);
368 if (ipv6_result == 0 && res != NULL) {
369 // Try IPv6 loopback connection
370 client->sockfd = socket_create("client_ipv6", res->ai_family, res->ai_socktype, res->ai_protocol);
371 if (client->sockfd != INVALID_SOCKET_VALUE) {
372 log_debug("Trying IPv6 loopback connection to [::1]:%s...", port_str);
373 if (connect_with_timeout(client->sockfd, res->ai_addr, res->ai_addrlen, CONNECT_TIMEOUT)) {
374 log_debug("Connection successful using IPv6 loopback");
375 SAFE_STRNCPY(client->server_ip, "::1", sizeof(client->server_ip));
376 freeaddrinfo(res);
377 res = NULL; // Prevent double-free at connection_success label
378 goto connection_success;
379 }
380 close_socket_safe(client->sockfd);
381 client->sockfd = INVALID_SOCKET_VALUE;
382 }
383 freeaddrinfo(res);
384 res = NULL;
385 }
386
387 // IPv6 failed, try IPv4 loopback (127.0.0.1)
388 log_debug("IPv6 failed, trying IPv4 loopback 127.0.0.1:%s...", port_str);
389 hints.ai_family = AF_INET;
390
391 int ipv4_result = getaddrinfo("127.0.0.1", port_str, &hints, &res);
392 if (ipv4_result == 0 && res != NULL) {
393 client->sockfd = socket_create("client_ipv4", res->ai_family, res->ai_socktype, res->ai_protocol);
394 if (client->sockfd != INVALID_SOCKET_VALUE) {
395 log_debug("Trying IPv4 loopback connection to 127.0.0.1:%s...", port_str);
396 if (connect_with_timeout(client->sockfd, res->ai_addr, res->ai_addrlen, CONNECT_TIMEOUT)) {
397 log_debug("Connection successful using IPv4 loopback");
398 SAFE_STRNCPY(client->server_ip, "127.0.0.1", sizeof(client->server_ip));
399 freeaddrinfo(res);
400 res = NULL;
401 goto connection_success;
402 }
403 close_socket_safe(client->sockfd);
404 client->sockfd = INVALID_SOCKET_VALUE;
405 }
406 freeaddrinfo(res);
407 res = NULL;
408 }
409
410 // Both IPv6 and IPv4 loopback failed for localhost
411 log_warn("Could not connect to localhost using either IPv6 or IPv4 loopback");
412 return -1;
413 }
414
415 // For non-localhost addresses, use standard resolution
416 log_debug("Resolving server address '%s' port %s...", address, port_str);
417 hints.ai_family = AF_UNSPEC;
418 hints.ai_flags = 0;
419 int getaddr_result = getaddrinfo(address, port_str, &hints, &res);
420 if (getaddr_result != 0) {
421 log_error("Failed to resolve server address '%s': %s", address, gai_strerror(getaddr_result));
422 return -1;
423 }
424
425 // Try each address returned by getaddrinfo() - prefer IPv6, fall back to IPv4
426 for (int address_family = AF_INET6; address_family >= AF_INET; address_family -= (AF_INET6 - AF_INET)) {
427 for (addr_iter = res; addr_iter != NULL; addr_iter = addr_iter->ai_next) {
428 if (addr_iter->ai_family != address_family) {
429 continue;
430 }
431
432 const char *socket_name = (addr_iter->ai_family == AF_INET6) ? "client_server_ipv6" : "client_server_ipv4";
433 client->sockfd = socket_create(socket_name, addr_iter->ai_family, addr_iter->ai_socktype, addr_iter->ai_protocol);
434 if (client->sockfd == INVALID_SOCKET_VALUE) {
435 continue;
436 }
437
438 if (addr_iter->ai_family == AF_INET) {
439 log_debug("Trying IPv4 connection...");
440 } else if (addr_iter->ai_family == AF_INET6) {
441 log_debug("Trying IPv6 connection...");
442 }
443
444 if (connect_with_timeout(client->sockfd, addr_iter->ai_addr, addr_iter->ai_addrlen, CONNECT_TIMEOUT)) {
445 log_debug("Connection successful using %s", addr_iter->ai_family == AF_INET ? "IPv4"
446 : addr_iter->ai_family == AF_INET6 ? "IPv6"
447 : "unknown protocol");
448
449 // Extract server IP address for known_hosts
450 if (format_ip_address(addr_iter->ai_family, addr_iter->ai_addr, client->server_ip, sizeof(client->server_ip)) ==
451 ASCIICHAT_OK) {
452 log_debug("Resolved server IP: %s", client->server_ip);
453 } else {
454 log_warn("Failed to format server IP address");
455 }
456
457 goto connection_success;
458 }
459
460 close_socket_safe(client->sockfd);
461 client->sockfd = INVALID_SOCKET_VALUE;
462 }
463 }
464
465connection_success:
466
467 if (res) {
468 freeaddrinfo(res);
469 }
470
471 // If we exhausted all addresses without success, fail
472 if (client->sockfd == INVALID_SOCKET_VALUE) {
473 log_warn("Could not connect to server %s:%d (tried all addresses)", address, port);
474 return -1;
475 }
476
477 // Extract local port for client ID
478 struct sockaddr_storage local_addr = {0};
479 socklen_t addr_len = sizeof(local_addr);
480 if (getsockname(client->sockfd, (struct sockaddr *)&local_addr, &addr_len) == -1) {
481 log_error("Failed to get local socket address: %s", network_error_string());
482 close_socket_safe(client->sockfd);
483 client->sockfd = INVALID_SOCKET_VALUE;
484 return -1;
485 }
486
487 // Extract port from either IPv4 or IPv6 address
488 int local_port = 0;
489 if (((struct sockaddr *)&local_addr)->sa_family == AF_INET) {
490 local_port = NET_TO_HOST_U16(((struct sockaddr_in *)&local_addr)->sin_port);
491 } else if (((struct sockaddr *)&local_addr)->sa_family == AF_INET6) {
492 local_port = NET_TO_HOST_U16(((struct sockaddr_in6 *)&local_addr)->sin6_port);
493 }
494 client->my_client_id = (uint32_t)local_port;
495
496 // Update registration name now that we have a client ID
497 {
498 char client_name[64];
499 SAFE_SNPRINTF(client_name, sizeof(client_name), "tcp_client_%u", client->my_client_id);
500 named_update_name((uintptr_t)client, client_name);
501 }
502
503 // Mark connection as active
504 atomic_store_bool(&client->connection_active, true);
505 atomic_store_bool(&client->connection_lost, false);
506 atomic_store_bool(&client->should_reconnect, false);
507
508 // Initialize crypto (application must set crypto_initialized flag)
509 // This is done outside this function by calling client_crypto_init()
510
511 // Configure socket options
512 if (socket_set_keepalive(client->sockfd, true) < 0) {
513 log_warn("Failed to set socket keepalive: %s", network_error_string());
514 }
515
516 asciichat_error_t sock_config_result = socket_configure_buffers(client->sockfd);
517 if (sock_config_result != ASCIICHAT_OK) {
518 log_warn("Failed to configure socket: %s", network_error_string());
519 }
520
521 log_debug("Connection established successfully to %s:%d (client_id=%u)", address, port, client->my_client_id);
522 return 0;
523}
#define NET_TO_HOST_U16(val)
Definition endian.h:111
unsigned int uint32_t
Definition common.h:58
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:414
#define SAFE_SNPRINTF(buffer, buffer_size,...)
Definition common.h:492
unsigned long long uint64_t
Definition common.h:59
const char * named_update_name(uintptr_t key, const char *new_base_name)
Update the registered name for a resource with a new base name.
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_INVALID_PARAM
#define log_warn(...)
Log a WARN message.
Definition log/log.h:574
#define log_error(...)
Log an ERROR message.
Definition log/log.h:587
#define CONNECT_TIMEOUT
Connection timeout in seconds (3 seconds)
bool connect_with_timeout(socket_t sockfd, const struct sockaddr *addr, socklen_t addrlen, int timeout_seconds)
Connect with timeout.
const char * network_error_string()
Get human-readable error string for network errors.
asciichat_error_t socket_configure_buffers(socket_t sockfd)
Configure socket buffers and TCP_NODELAY for optimal performance.
void platform_sleep_ns(uint64_t ns)
Platform-safe sleep function with nanosecond precision.
int socket_set_keepalive(socket_t sock, bool keepalive)
Set SO_KEEPALIVE socket option.
socket_t socket_create(const char *name, int domain, int type, int protocol)
Create a new named socket.
int is_localhost_ipv6(const char *ip)
Check if IPv6 address is localhost.
Definition ip.c:1320
int is_localhost_ipv4(const char *ip)
Check if IPv4 address is localhost.
Definition ip.c:1299
asciichat_error_t format_ip_address(int family, const struct sockaddr *addr, char *output, size_t output_size)
Format IP address from socket address structure.
Definition ip.c:196

References ASCIICHAT_OK, atomic_store_bool(), CONNECT_TIMEOUT, connect_with_timeout(), ERROR_INVALID_PARAM, format_ip_address(), INVALID_SOCKET_VALUE, is_localhost_ipv4(), is_localhost_ipv6(), log_debug, log_error, log_warn, named_update_name(), NET_TO_HOST_U16, network_error_string(), platform_sleep_ns(), SAFE_SNPRINTF, SAFE_STRNCPY, SET_ERRNO, socket_configure_buffers(), socket_create(), socket_is_valid(), and socket_set_keepalive().

Referenced by connection_attempt_tcp().

◆ tcp_client_create()

tcp_client_t * tcp_client_create ( void  )

Create and initialize a TCP client instance.

Allocates a new tcp_client_t structure and initializes all fields to safe defaults. This function must be called before starting any worker threads.

Initialization Steps

  1. Allocate tcp_client_t structure
  2. Zero-initialize all fields
  3. Set atomic flags to initial states
  4. Initialize mutexes and condition variables
  5. Set socket to INVALID_SOCKET_VALUE

Error Handling

Returns NULL if allocation fails or mutex initialization fails. Check errno or use HAS_ERRNO() for detailed error information.

Returns
Pointer to initialized client, or NULL on failure
Note
Caller must call tcp_client_destroy() when done
See also
tcp_client_destroy() For proper cleanup

Create and initialize a TCP client instance.

Allocates tcp_client_t and initializes all fields to safe defaults.

Definition at line 96 of file lib/network/tcp/client.c.

96 {
98 if (!client) {
99 log_error("Failed to allocate tcp_client_t");
100 return NULL;
101 }
102
103 memset(client, 0, sizeof(*client));
104
105 client->sockfd = INVALID_SOCKET_VALUE;
106 atomic_store_bool(&client->connection_active, false);
107 atomic_store_bool(&client->connection_lost, false);
108 atomic_store_bool(&client->should_reconnect, false);
109 client->my_client_id = 0;
110 memset(client->server_ip, 0, sizeof(client->server_ip));
111 client->encryption_enabled = false;
112
113 if (mutex_init(&client->send_mutex, "client_send") != 0) {
114 log_error("Failed to initialize send mutex");
115 SAFE_FREE(client);
116 return NULL;
117 }
118
119 NAMED_REGISTER_CLIENT(client, "client", NULL);
120
121 // Register atomic fields for debug tracking - parent_key handles hierarchical naming
122 NAMED_REGISTER_ATOMIC(&client->connection_active, "is_connection_active", (uintptr_t)(const void *)(client));
123 NAMED_REGISTER_ATOMIC(&client->connection_lost, "has_connection_been_lost", (uintptr_t)(const void *)(client));
124 NAMED_REGISTER_ATOMIC(&client->should_reconnect, "should_attempt_reconnection", (uintptr_t)(const void *)(client));
125
126 log_debug("TCP client created successfully");
127 return client;
128}
void tcp_client_t
#define SAFE_FREE(ptr)
Definition common.h:376
#define SAFE_MALLOC(size, cast)
Definition common.h:264
#define NAMED_REGISTER_ATOMIC(a, name, parent_ptr)
Register an atomic_t with automatic format specifier.
#define NAMED_REGISTER_CLIENT(client, name, parent_ptr)
Register a client connection with automatic format specifier.
int mutex_init(mutex_t *mutex, const char *name)
Initialize a mutex with a name.
Definition threading.c:16

References atomic_store_bool(), INVALID_SOCKET_VALUE, log_debug, log_error, mutex_init(), NAMED_REGISTER_ATOMIC, NAMED_REGISTER_CLIENT, SAFE_FREE, and SAFE_MALLOC.

Referenced by connection_attempt_tcp().

◆ tcp_client_destroy()

void tcp_client_destroy ( tcp_client_t **  client_ptr)

Destroy TCP client and free all resources.

Destroys all synchronization primitives and frees the client structure. This function must be called AFTER all worker threads have been joined.

Cleanup Steps

  1. Verify all threads have exited (debug builds only)
  2. Destroy all mutexes and condition variables
  3. Free tcp_client_t structure
  4. Set pointer to NULL (via parameter)

Thread Safety

This function is NOT thread-safe. All worker threads must be joined before calling this function.

Parameters
client_ptrPointer to client pointer (set to NULL after free)
Warning
All threads must be stopped and joined before calling
Note
No-op if client_ptr is NULL or *client_ptr is NULL

Destroy TCP client and free all resources.

Must be called AFTER all threads have been joined.

Definition at line 135 of file lib/network/tcp/client.c.

135 {
136 if (!client_ptr || !*client_ptr) {
137 return;
138 }
139
140 tcp_client_t *client = *client_ptr;
141
142 if (socket_is_valid(client->sockfd)) {
143 close_socket_safe(client->sockfd);
144 client->sockfd = INVALID_SOCKET_VALUE;
145 }
146
147 // Unregister atomic fields
148 NAMED_UNREGISTER(&client->connection_active);
149 NAMED_UNREGISTER(&client->connection_lost);
150 NAMED_UNREGISTER(&client->should_reconnect);
151
152 NAMED_UNREGISTER(client);
153 mutex_destroy(&client->send_mutex);
154 SAFE_FREE(*client_ptr);
155
156 log_debug("TCP client destroyed");
157}
#define NAMED_UNREGISTER(ptr)
Unregister a pointer.
int mutex_destroy(mutex_t *mutex)
Destroy a mutex.
Definition threading.c:22

References INVALID_SOCKET_VALUE, log_debug, mutex_destroy(), NAMED_UNREGISTER, SAFE_FREE, and socket_is_valid().

Referenced by connection_attempt_tcp().

◆ tcp_client_get_id()

uint32_t tcp_client_get_id ( const tcp_client_t *  client)

Get client ID assigned by server.

Parameters
clientTCP client instance
Returns
Client ID (from local port) or 0 if not connected

Definition at line 191 of file lib/network/tcp/client.c.

191 {
192 return client ? client->my_client_id : 0;
193}

◆ tcp_client_get_socket()

socket_t tcp_client_get_socket ( const tcp_client_t *  client)

Get current socket descriptor.

Parameters
clientTCP client instance
Returns
Socket descriptor or INVALID_SOCKET_VALUE if not connected

Definition at line 184 of file lib/network/tcp/client.c.

184 {
185 return client ? client->sockfd : INVALID_SOCKET_VALUE;
186}

References INVALID_SOCKET_VALUE.

Referenced by connection_attempt_tcp().

◆ tcp_client_is_active()

bool tcp_client_is_active ( const tcp_client_t *  client)

Check if connection is currently active.

Parameters
clientTCP client instance
Returns
true if connection is active, false otherwise

Definition at line 166 of file lib/network/tcp/client.c.

166 {
167 if (!client)
168 return false;
169 return atomic_load_bool(&client->connection_active);
170}
bool atomic_load_bool(atomic_t *a)
Atomically load a boolean value.
Definition atomic.c:169

References atomic_load_bool().

◆ tcp_client_is_lost()

bool tcp_client_is_lost ( const tcp_client_t *  client)

Check if connection was lost.

Parameters
clientTCP client instance
Returns
true if connection loss was detected, false otherwise

Definition at line 175 of file lib/network/tcp/client.c.

175 {
176 if (!client)
177 return false;
178 return atomic_load_bool(&client->connection_lost);
179}

References atomic_load_bool().

◆ tcp_client_send_packet()

int tcp_client_send_packet ( tcp_client_t *  client,
packet_type_t  type,
const void *  data,
size_t  len 
)

Send packet with thread-safe mutex protection.

All packet transmission goes through this function to ensure packets aren't interleaved on the wire. Automatically handles encryption if crypto context is ready.

Parameters
clientTCP client instance
typePacket type identifier
dataPacket payload
lenPayload length
Returns
0 on success, negative on error

All packet transmission goes through this function to ensure packets aren't interleaved on the wire.

Definition at line 260 of file lib/network/tcp/client.c.

260 {
261 if (!client) {
262 return SET_ERRNO(ERROR_INVALID_PARAM, "NULL client");
263 }
264
265 if (!atomic_load_bool(&client->connection_active)) {
266 return SET_ERRNO(ERROR_NETWORK, "Connection not active");
267 }
268
269 // Acquire send mutex for thread-safe transmission
270 mutex_lock(&client->send_mutex);
271
272 // Send packet without encryption (crypto is handled at app_client layer)
273 asciichat_error_t result = (asciichat_error_t)send_packet(client->sockfd, type, data, len);
274
275 mutex_unlock(&client->send_mutex);
276
277 if (result != ASCIICHAT_OK) {
278 log_debug("Failed to send packet type %d: %s", type, asciichat_error_string(result));
279 return -1;
280 }
281
282 return 0;
283}
@ ERROR_NETWORK
Definition error_codes.h:77
int send_packet(socket_t sockfd, packet_type_t type, const void *data, size_t len)
Send a basic packet without encryption.
Definition packet.c:784
#define mutex_lock(mutex)
Lock a mutex (with debug tracking in debug builds)
#define mutex_unlock(mutex)
Unlock a mutex (with debug tracking in debug builds)

References ASCIICHAT_OK, atomic_load_bool(), ERROR_INVALID_PARAM, ERROR_NETWORK, log_debug, mutex_lock, mutex_unlock, send_packet(), and SET_ERRNO.

Referenced by tcp_client_send_ping(), and tcp_client_send_pong().

◆ tcp_client_send_ping()

int tcp_client_send_ping ( tcp_client_t *  client)

Send ping packet.

Parameters
clientTCP client instance
Returns
0 on success, negative on error

Definition at line 288 of file lib/network/tcp/client.c.

288 {
289 if (!client)
290 return -1;
291 return tcp_client_send_packet(client, PACKET_TYPE_PING, NULL, 0);
292}
@ PACKET_TYPE_PING
Keepalive ping packet.
Definition packet.h:383
int tcp_client_send_packet(tcp_client_t *client, packet_type_t type, const void *data, size_t len)
Send packet with thread-safe mutex protection.

References PACKET_TYPE_PING, and tcp_client_send_packet().

◆ tcp_client_send_pong()

int tcp_client_send_pong ( tcp_client_t *  client)

Send pong packet.

Parameters
clientTCP client instance
Returns
0 on success, negative on error

Definition at line 297 of file lib/network/tcp/client.c.

297 {
298 if (!client)
299 return -1;
300 return tcp_client_send_packet(client, PACKET_TYPE_PONG, NULL, 0);
301}
@ PACKET_TYPE_PONG
Keepalive pong response.
Definition packet.h:385

References PACKET_TYPE_PONG, and tcp_client_send_packet().

◆ tcp_client_shutdown()

void tcp_client_shutdown ( tcp_client_t *  client)

Shutdown connection forcefully (for signal handlers)

Parameters
clientTCP client instance

Definition at line 238 of file lib/network/tcp/client.c.

238 {
239 if (!client)
240 return;
241
242 atomic_store_bool(&client->connection_active, false);
243
244 // Shutdown socket for reading/writing to interrupt blocking calls
245 if (socket_is_valid(client->sockfd)) {
246 socket_shutdown(client->sockfd, SHUT_RDWR);
247 }
248}
int socket_shutdown(socket_t sock, int how)
Shutdown socket I/O.

References atomic_store_bool(), socket_is_valid(), and socket_shutdown().

◆ tcp_client_signal_lost()

void tcp_client_signal_lost ( tcp_client_t *  client)

Signal that connection was lost (triggers reconnection)

Parameters
clientTCP client instance

Definition at line 202 of file lib/network/tcp/client.c.

202 {
203 if (!client)
204 return;
205
206 if (!atomic_load_bool(&client->connection_lost)) {
207 atomic_store_bool(&client->connection_lost, true);
208 atomic_store_bool(&client->connection_active, false);
209 log_info("Connection lost signaled");
210 }
211}
#define log_info(...)
Log an INFO message.
Definition log/log.h:561

References atomic_load_bool(), atomic_store_bool(), and log_info.