ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
Connection Management

πŸ”— Server connection establishment, packet transmission, and state tracking More...

Files

file  server.c
 πŸŒ Client connection manager: TCP connection, reconnection with exponential backoff, and thread-safe transmission
 
file  server.h
 ascii-chat Client Server Connection Management Interface
 

Functions

int server_connection_init ()
 Initialize the server connection management subsystem.
 
int server_connection_establish (const char *address, int port, int reconnect_attempt, bool first_connection, bool has_ever_connected)
 Establish connection to ascii-chat server.
 
bool server_connection_is_active ()
 Check if server connection is currently active.
 
socket_t server_connection_get_socket ()
 Get current socket file descriptor.
 
acip_transport_t * server_connection_get_transport (void)
 Get ACIP transport instance.
 
void server_connection_set_transport (acip_transport_t *transport)
 Set ACIP transport instance from connection fallback.
 
uint32_t server_connection_get_client_id ()
 Get client ID assigned by server.
 
const char * server_connection_get_ip ()
 Get resolved server IP address.
 
void server_connection_set_ip (const char *ip)
 Set the server IP address.
 
void server_connection_set_port (int port)
 Set the server port.
 
int server_connection_get_port (void)
 Get the server port.
 
void server_connection_close ()
 Close the server connection gracefully.
 
void server_connection_shutdown ()
 Emergency connection shutdown for signal handlers.
 
void server_connection_lost ()
 Signal that connection has been lost.
 
bool server_connection_is_lost ()
 Check if connection loss has been detected.
 
void server_connection_cleanup ()
 Cleanup connection management subsystem.
 
asciichat_error_t threaded_send_packet (packet_type_t type, const void *data, size_t len)
 Thread-safe packet transmission.
 
int threaded_send_audio_batch_packet (const float *samples, int num_samples, int batch_count)
 Thread-safe batched audio packet transmission.
 
asciichat_error_t threaded_send_audio_opus (const uint8_t *opus_data, size_t opus_size, int sample_rate, int frame_duration)
 Thread-safe Opus audio frame transmission.
 
asciichat_error_t threaded_send_audio_opus_batch (const uint8_t *opus_data, size_t opus_size, const uint16_t *frame_sizes, int frame_count)
 Thread-safe Opus audio batch packet transmission.
 
asciichat_error_t threaded_send_image_frame (const void *pixel_data, uint32_t width, uint32_t height, uint32_t pixel_format)
 Thread-safe image frame transmission.
 
asciichat_error_t threaded_send_image_frame_h265 (const void *pixel_data, uint32_t width, uint32_t height)
 Send H.265-encoded video frame to server (thread-safe)
 
int threaded_send_ping_packet (void)
 Thread-safe ping packet transmission.
 
int threaded_send_pong_packet (void)
 Thread-safe pong packet transmission.
 
asciichat_error_t threaded_send_stream_start_packet (uint32_t stream_type)
 Thread-safe stream start packet transmission.
 
asciichat_error_t threaded_send_terminal_size_with_auto_detect (unsigned short width, unsigned short height)
 Thread-safe terminal size packet transmission with auto-detection.
 
int threaded_send_client_join_packet (const char *display_name, uint32_t capabilities)
 Thread-safe client join packet transmission.
 
int server_send_packet (packet_type_t type, const void *data, size_t len)
 Send general packet to server.
 
int server_send_audio (const float *samples, int num_samples)
 Send audio data to server.
 
int server_send_audio_batch (const float *samples, int num_samples, int batch_count)
 Send batched audio data to server.
 
int server_send_terminal_capabilities (unsigned short width, unsigned short height)
 Send terminal capabilities to server.
 
int server_send_ping ()
 Send ping keepalive packet.
 
int server_send_pong ()
 Send pong response packet.
 
int server_send_stream_start (uint32_t stream_type)
 Send stream start notification.
 
int server_send_stream_stop (uint32_t stream_type)
 Send stream stop notification.
 

Variables

crypto_handshake_context_t g_crypto_ctx = {0}
 Per-connection crypto handshake context.
 

Detailed Description

πŸ”— Server connection establishment, packet transmission, and state tracking

Connection Management

Overview

The connection management subsystem handles TCP socket creation, connection establishment, cryptographic handshake coordination, and thread-safe packet transmission to the server.

Implementation: src/client/server.c, src/client/server.h

Connection State

Global State Variables:

  • g_sockfd: Socket file descriptor
  • g_client_id: Server-assigned client ID
  • g_server_ip: Resolved server IP address
  • g_connection_active: Atomic boolean for connection status
  • g_connection_lost: Atomic boolean for connection loss detection
  • g_send_mutex: Mutex protecting all packet sends

Connection Establishment

Connection Flow

int server_connection_establish(const char *address, int port,
int reconnect_attempt, bool first_connection,
bool has_ever_connected)
{
// 1. Create socket
socket_t sockfd = socket(AF_INET, SOCK_STREAM, 0);
// 2. Resolve address (IPv4/IPv6)
struct addrinfo *result = resolve_address(address, port);
// 3. Connect with timeout
if (connect(sockfd, result->ai_addr, result->ai_addrlen) < 0) {
}
// 4. Perform cryptographic handshake
crypto_handshake_result_t handshake_result =
perform_crypto_handshake_client(sockfd, &crypto_ctx, opt_server_key,
server_ip, opt_client_key);
if (handshake_result != HANDSHAKE_SUCCESS &&
handshake_result != HANDSHAKE_WARNING_NO_CLIENT_AUTH) {
socket_close(sockfd);
return map_handshake_error(handshake_result);
}
// 5. Send client capabilities
send_client_capabilities();
// 6. Update global state
g_sockfd = sockfd;
atomic_store(&g_connection_active, true);
atomic_store(&g_connection_lost, false);
}
int server_connection_establish(const char *address, int port, int reconnect_attempt, bool first_connection, bool has_ever_connected)
Establish connection to ascii-chat server.
int socket_close(socket_t sock)
Close a socket.
int socket_t
@ CONNECTION_ERROR_GENERIC
Generic error (retry allowed)
@ CONNECTION_SUCCESS
Connection established successfully.

Connection Error Codes

Error Types:

  • CONNECTION_SUCCESS (0): Connection established successfully
  • CONNECTION_WARNING_NO_CLIENT_AUTH (1): Connected but server not verifying client
  • CONNECTION_ERROR_GENERIC (-1): Network error (allow retry)
  • CONNECTION_ERROR_AUTH_FAILED (-2): Client authentication failed (no retry)
  • CONNECTION_ERROR_HOST_KEY_FAILED (-3): Server host key verification failed (no retry)

Packet Transmission

Thread-Safe Packet Send

int send_packet_to_server(packet_type_t type, const void *data, size_t data_len,
uint32_t client_id)
{
// Thread-safe send with global mutex
mutex_lock(&g_send_mutex);
// Check connection status
mutex_unlock(&g_send_mutex);
return -1;
}
// Send encrypted packet
int result = send_packet(g_sockfd, &crypto_ctx, type, data, data_len, client_id);
mutex_unlock(&g_send_mutex);
// Detect connection loss on send error
if (result < 0) {
}
return result;
}
bool server_connection_is_active()
Check if server connection is currently active.
void server_connection_lost()
Signal that connection has been lost.
unsigned int uint32_t
Definition common.h:58
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
packet_type_t
Network protocol packet type enumeration.
Definition packet.h:286
#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)

Thread Safety:

  • ALL packet sends MUST go through send_packet_to_server()
  • Global g_send_mutex prevents interleaved packets
  • Socket FD checked under mutex protection
  • Connection loss detected atomically

Connection Loss Detection

Loss Detection Triggers

Connection loss detected by:

  1. Socket write error during send_packet_to_server()
  2. Socket read error during receive_packet() in protocol thread
  3. Keepalive timeout (no pong for 30 seconds)
  4. Decryption failure (corrupted stream)

Loss Handling

{
// Atomic compare-exchange ensures only first detection triggers action
bool expected = true;
if (atomic_compare_exchange_strong(&g_connection_active, &expected, false)) {
// First thread to detect loss
atomic_store(&g_connection_lost, true);
log_warn("Connection lost detected");
}
}
#define log_warn(...)
Log a WARN message.
Definition log/log.h:574

Connection Cleanup

Graceful Close

{
if (g_sockfd != INVALID_SOCKET_VALUE) {
socket_shutdown(g_sockfd, SHUT_RDWR); // Signal shutdown
socket_close(g_sockfd); // Close socket
}
atomic_store(&g_connection_active, false);
g_client_id = 0;
}
void server_connection_close()
Close the server connection gracefully.
int socket_shutdown(socket_t sock, int how)
Shutdown socket I/O.
#define INVALID_SOCKET_VALUE
Invalid socket value (POSIX: -1)
Definition socket.h:278

Emergency Shutdown

{
// Signal-safe emergency shutdown
if (g_sockfd != INVALID_SOCKET_VALUE) {
socket_close(g_sockfd); // Immediate close (no shutdown)
}
}
void server_connection_shutdown()
Emergency connection shutdown for signal handlers.

Best Practices

DO:

  • Always use send_packet_to_server() for packet transmission
  • Check server_connection_is_active() before operations
  • Use atomic operations for connection state flags
  • Wait for thread exit before reconnection

DON'T:

  • Don't send packets without g_send_mutex
  • Don't close socket while threads may be using it
  • Don't ignore return values from send operations
  • Don't modify g_sockfd directly (use provided functions)
See also
src/client/server.c
src/client/server.h
Client Overview

Function Documentation

◆ server_connection_cleanup()

void server_connection_cleanup ( )

#include <server.c>

Cleanup connection management subsystem.

Closes any active connection and destroys synchronization objects. Called during client shutdown.

Definition at line 958 of file src/client/server.c.

958 {
959 if (!GET_OPTION(quiet)) {
961 }
963 mutex_destroy(&g_send_mutex);
964}
void log_set_terminal_output(bool enabled)
Control stderr output to terminal.
Definition log/log.c:700
#define GET_OPTION(field)
Safely get a specific option field (lock-free read)
int mutex_destroy(mutex_t *mutex)
Destroy a mutex.
Definition threading.c:22

References GET_OPTION, log_set_terminal_output(), mutex_destroy(), and server_connection_close().

◆ server_connection_close()

void server_connection_close ( )

#include <server.c>

Close the server connection gracefully.

Close server connection gracefully.

Marks connection as inactive and closes the socket. This function is safe to call multiple times and from multiple threads.

Definition at line 809 of file src/client/server.c.

809 {
810 log_debug("[TRANSPORT_LIFECYCLE] server_connection_close() called");
811
812 // Acquire send mutex to prevent race with threaded_send_image_frame()
813 // This ensures any in-flight sends complete before we destroy the transport
814 mutex_lock(&g_send_mutex);
815 atomic_store_bool(&g_connection_active, false);
816
817 // Destroy ACIP transport before closing socket
818 if (g_client_transport) {
819 log_debug("[TRANSPORT_LIFECYCLE] Destroying transport=%p (is_connected=%s) before closing socket",
820 (void *)g_client_transport, acip_transport_is_connected(g_client_transport) ? "true" : "false");
821 acip_transport_destroy(g_client_transport);
822 g_client_transport = NULL;
823 log_debug("[TRANSPORT_LIFECYCLE] Transport destroyed, g_client_transport set to NULL");
824 }
825
826 mutex_unlock(&g_send_mutex);
827
828 // The socket belongs to the connection handle's TCP/WebSocket owner. The
829 // caller cleans up that owner after protocol workers have stopped; closing
830 // g_sockfd here would close the same descriptor a second time.
831 g_sockfd = INVALID_SOCKET_VALUE;
832
833 g_my_client_id = 0;
834
835 // Cleanup crypto context if encryption was enabled
836 if (g_encryption_enabled) {
837 log_debug("[TRANSPORT_LIFECYCLE] Cleaning up crypto context");
839 g_encryption_enabled = false;
840 }
841
842 // Turn ON terminal logging when connection is closed (unless it was disabled with --quiet)
843 if (!GET_OPTION(quiet)) {
845 }
846 log_debug("[TRANSPORT_LIFECYCLE] server_connection_close() completed");
847}
void atomic_store_bool(atomic_t *a, bool value)
Atomically store a boolean value.
Definition atomic.c:177
void crypto_handshake_destroy(crypto_handshake_context_t *ctx)
Cleanup crypto handshake context with secure memory wiping.
crypto_handshake_context_t g_crypto_ctx
Per-connection crypto handshake context.
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548
void acip_transport_destroy(acip_transport_t *transport)
Destroy transport and free all resources.

References acip_transport_destroy(), atomic_store_bool(), crypto_handshake_destroy(), g_crypto_ctx, GET_OPTION, INVALID_SOCKET_VALUE, log_debug, log_set_terminal_output(), mutex_lock, and mutex_unlock.

Referenced by server_connection_cleanup().

◆ server_connection_establish()

int server_connection_establish ( const char *  address,
int  port,
int  reconnect_attempt,
bool  first_connection,
bool  has_ever_connected 
)

#include <server.c>

Establish connection to ascii-chat server.

Attempts to connect to the specified server with full capability negotiation. Implements reconnection logic with exponential backoff for failed attempts. On successful connection, performs initial handshake including terminal capabilities and client join protocol.

Parameters
addressServer IP address or hostname
portServer port number
reconnect_attemptCurrent reconnection attempt number (0 for first)
first_connectionTrue if this is the initial connection attempt
has_ever_connectedTrue if a connection was ever successfully established
Returns
0 on success, negative on error

Establishes a TCP connection to the server and performs the cryptographic handshake. This function handles DNS resolution, socket creation, connection establishment, and authentication. It is designed for use in a reconnection loop with exponential backoff.

Parameters
addressServer IP address, hostname, or ACDS session string (e.g., "localhost", "192.168.1.100", or "blue-mountain-tiger")
portServer port number (typically 27224)
reconnect_attemptCurrent reconnection attempt number (0 for first attempt). Used to track retry count for logging and backoff calculation.
first_connectionWhether this is the client's first attempt to connect. If true, displays more informative messages. If false, this is a reconnection attempt after previous connection loss.
has_ever_connectedWhether the client has EVER successfully connected in this session. Used to distinguish "first attempt ever" from "reconnection attempt".
Returns
Connection establishment status:
  • CONNECTION_SUCCESS (0): Connection established and authenticated successfully
  • CONNECTION_WARNING_NO_CLIENT_AUTH (1): Connected but server is not verifying client identity
  • CONNECTION_ERROR_GENERIC (-1): Network error (resolve failed, connect refused, timeout). Caller should retry with exponential backoff.
  • CONNECTION_ERROR_AUTH_FAILED (-2): Authentication failed (wrong password, bad SSH key). Caller should NOT retry - user intervention required.
  • CONNECTION_ERROR_HOST_KEY_FAILED (-3): Server host key verification failed. Caller should NOT retry - user must update known_hosts.

Connection Lifecycle

  1. Resolution: Resolves hostname/IP to socket address (supports IPv4 and IPv6)
  2. Socket Creation: Creates TCP socket with appropriate options
  3. Connection: Attempts TCP connect with timeout
  4. Handshake: Performs cryptographic handshake (X25519 key exchange, Ed25519 auth)
  5. Initialization: Sends client capabilities and client join packet
  6. State Update: Updates global connection state atomically

Error Recovery

Retryable (CONNECTION_ERROR_GENERIC):

  • Connection refused: Server not listening yet
  • Connection timeout: Network slow or firewall blocking
  • DNS resolution failure: Temporary DNS issue
  • Socket creation failure: System resource exhaustion

Caller should implement exponential backoff:

int result = server_connection_establish(address, port, attempt, first, ever_connected);
if (result == CONNECTION_ERROR_GENERIC) {
// Exponential backoff: 10ms, 210ms, 410ms, 610ms, ..., capped at 5 seconds
int delay_ms = 10 + (200 * attempt);
if (delay_ms > 5000) delay_ms = 5000;
sleep_ms(delay_ms);
attempt++;
continue; // Retry
}

Non-Retryable (AUTH or HOST_KEY failures):

  • CONNECTION_ERROR_AUTH_FAILED: Password or SSH key doesn't match server's expectations
  • CONNECTION_ERROR_HOST_KEY_FAILED: Server's public key not in client's known_hosts

These indicate configuration problems that won't be fixed by waiting or retrying.

See also
server_connection_is_active "Check connection status"
server_connection_close "Close connection gracefully"
server_connection_lost "Signal connection loss (called by other threads)"
topic_client_connection "Connection Management Architecture"

Definition at line 355 of file src/client/server.c.

356 {
357 (void)first_connection; // Currently unused
358 if (!address || port <= 0) {
359 log_error("Invalid address or port parameters");
360 return -1;
361 }
362
363 // Close any existing connection
364 if (g_sockfd != INVALID_SOCKET_VALUE) {
365 close_socket(g_sockfd);
366 g_sockfd = INVALID_SOCKET_VALUE;
367 }
368
369 // Apply reconnection delay if this is a retry
370 if (reconnect_attempt > 0) {
371 unsigned int delay_us = get_reconnect_delay(reconnect_attempt);
372 // Reconnection attempt logged only to file
373 platform_sleep_us(delay_us);
374
375 // Check if user requested exit during reconnection delay
376 if (should_exit()) {
377 log_debug("Exit requested during reconnection delay");
378 return -1;
379 }
380 } else {
381 // Initial connection logged only to file
382 }
383
384 connection_endpoint_t endpoint = {0};
385 asciichat_error_t endpoint_result = connection_endpoint_resolve(address, port, &endpoint);
386 if (endpoint_result != ASCIICHAT_OK) {
387 log_error("Failed to resolve connection endpoint: %s", asciichat_error_string(endpoint_result));
388 return -1;
389 }
390
391 // Check for WebSocket URL - handle separately from TCP
392 if (endpoint.protocol == CONNECTION_ENDPOINT_WEBSOCKET) {
393 // WebSocket connection - bypass TCP socket creation
394 // Use the original address (URL already contains port if specified)
395 const char *ws_url = endpoint.input;
396 log_info("Connecting via WebSocket: %s (host=%s, port=%u)", ws_url, endpoint.host, endpoint.port);
397
398 // Set server IP and port for crypto handshake
399 // This ensures the crypto context has proper server address info for known_hosts verification
402 log_debug("CLIENT_CONNECT: Set server IP=%s, port=%u from WebSocket endpoint", endpoint.host, endpoint.port);
403
404 // Initialize crypto if encryption is enabled
405 log_debug("CLIENT_CONNECT: Calling client_crypto_init()");
406 if (client_crypto_init() != 0) {
407 log_error("Failed to initialize crypto (password required or incorrect)");
408 log_debug("CLIENT_CONNECT: client_crypto_init() failed");
410 }
411
412 // Get crypto context for transport
414
415 // Create WebSocket transport through the shared factory.
416 asciichat_error_t ws_result =
417 connection_factory_open("client", ws_url, 0, (crypto_context_t *)crypto_ctx, &g_client_transport, &endpoint);
418 if (ws_result != ASCIICHAT_OK) {
419 log_error("Failed to create WebSocket ACIP transport");
420 return -1;
421 }
422 if (!g_client_transport) {
423 log_error("Failed to create WebSocket ACIP transport");
424 return -1;
425 }
426 log_debug("CLIENT_CONNECT: Created WebSocket ACIP transport with crypto context");
427
428 // Set connection as active
429 atomic_store_bool(&g_connection_active, true);
430 atomic_store_bool(&g_connection_lost, false);
431
432 // CRITICAL: Wait for WebSocket connection to be established before sending
433 // The service thread is running but may not have completed TLS handshake yet
434 // Don't block indefinitely - use timeout loops to allow responsiveness
435 log_debug("CLIENT_CONNECT: Waiting for WebSocket connection to be established...");
436 const int CONNECT_CHECK_ATTEMPTS = 300; // 300 * 50ms = 15 seconds timeout
437 int connect_attempts = 0;
438 while (!acip_transport_is_connected(g_client_transport) && connect_attempts < CONNECT_CHECK_ATTEMPTS) {
439 platform_sleep_us(50000); // Sleep 50ms between checks
440 connect_attempts++;
441 if (connect_attempts % 20 == 0) { // Log every 1 second (20 * 50ms)
442 log_info("CLIENT_CONNECT: Waiting for connection... (%d seconds elapsed)", connect_attempts / 20);
443 }
444 }
445
446 if (!acip_transport_is_connected(g_client_transport)) {
447 log_error("Failed to establish WebSocket connection within 15 seconds");
448 acip_transport_destroy(g_client_transport);
449 g_client_transport = NULL;
450 atomic_store_bool(&g_connection_active, false);
451 return -1;
452 }
453
454 log_info("CLIENT_CONNECT: WebSocket connection established, proceeding with handshake");
455
456 // Send initial terminal capabilities to server
459 if (result < 0) {
460 log_error("Failed to send initial capabilities to server: %s", network_error_string());
461 acip_transport_destroy(g_client_transport);
462 g_client_transport = NULL;
463 return -1;
464 }
465
466 // Disable terminal logging after initial setup (for non-snapshot mode)
467 if (!GET_OPTION(snapshot_mode) && has_ever_connected) {
469 }
470
471 // Build capabilities flags
472 uint32_t my_capabilities = CLIENT_CAP_VIDEO;
473 log_debug("GET_OPTION(audio_enabled) = %d (sending CLIENT_JOIN)", GET_OPTION(audio_enabled));
474 if (GET_OPTION(audio_enabled)) {
475 log_debug("Adding CLIENT_CAP_AUDIO to capabilities");
476 my_capabilities |= CLIENT_CAP_AUDIO;
477 }
478 if (GET_OPTION(color_mode) != COLOR_MODE_NONE) {
479 my_capabilities |= CLIENT_CAP_COLOR;
480 }
481 if (GET_OPTION(stretch)) {
482 my_capabilities |= CLIENT_CAP_STRETCH;
483 }
484
485 // Generate display name from username + PID
486 const char *display_name = platform_get_username();
487 char my_display_name[MAX_DISPLAY_NAME_LEN];
488 int pid = getpid();
489 SAFE_SNPRINTF(my_display_name, sizeof(my_display_name), "%s-%d", display_name, pid);
490 if (threaded_send_client_join_packet(my_display_name, my_capabilities) < 0) {
491 log_error("Failed to send client join packet: %s", network_error_string());
492 acip_transport_destroy(g_client_transport);
493 g_client_transport = NULL;
494 return -1;
495 }
496
497 log_info("WebSocket connection established successfully");
498 return 0;
499 }
500
501 asciichat_error_t connect_result =
502 connection_factory_open("client", address, (uint16_t)port, NULL, &g_client_transport, &endpoint);
503 if (connect_result != ASCIICHAT_OK || !g_client_transport) {
504 log_error("Failed to establish TCP transport: %s", asciichat_error_string(connect_result));
505 return -1;
506 }
507
508 g_sockfd = g_client_transport->methods->get_socket(g_client_transport);
509 if (g_sockfd == INVALID_SOCKET_VALUE) {
510 log_error("TCP transport did not expose a valid socket");
511 acip_transport_destroy(g_client_transport);
512 g_client_transport = NULL;
513 return -1;
514 }
515
516 SAFE_STRNCPY(g_server_ip, endpoint.host, sizeof(g_server_ip));
517 log_debug("CLIENT_CONNECT: Resolved TCP endpoint host=%s port=%u", endpoint.host, endpoint.port);
518
519 // Connection successful - extract local port for client ID
520 struct sockaddr_storage local_addr = {0};
521 socklen_t addr_len = sizeof(local_addr);
522 if (getsockname(g_sockfd, (struct sockaddr *)&local_addr, &addr_len) == -1) {
523 log_error("Failed to get local socket address: %s", network_error_string());
524 close_socket(g_sockfd);
525 g_sockfd = INVALID_SOCKET_VALUE;
526 return -1;
527 }
528
529 // Extract port from either IPv4 or IPv6 address
530 int local_port = 0;
531 if (((struct sockaddr *)&local_addr)->sa_family == AF_INET) {
532 local_port = NET_TO_HOST_U16(((struct sockaddr_in *)&local_addr)->sin_port);
533 } else if (((struct sockaddr *)&local_addr)->sa_family == AF_INET6) {
534 local_port = NET_TO_HOST_U16(((struct sockaddr_in6 *)&local_addr)->sin6_port);
535 }
536 g_my_client_id = (uint32_t)local_port;
537
538 // Mark connection as active immediately after successful socket connection
539 atomic_store_bool(&g_connection_active, true);
540 atomic_store_bool(&g_connection_lost, false);
541 atomic_store_bool(&g_should_reconnect, false);
542
543 // Initialize crypto BEFORE starting protocol handshake
544 // Note: server IP is already set above in the connection loop
545 log_debug("CLIENT_CONNECT: Calling client_crypto_init()");
546 if (client_crypto_init() != 0) {
547 log_error("Failed to initialize crypto (password required or incorrect)");
548 log_debug("CLIENT_CONNECT: client_crypto_init() failed");
549 close_socket(g_sockfd);
550 g_sockfd = INVALID_SOCKET_VALUE;
551 return CONNECTION_ERROR_AUTH_FAILED; // SSH key password was wrong - no retry
552 }
553
554 // Create the persistent TCP transport before handshake.
555 // It starts with NULL crypto; client_crypto_handshake() sets crypto_ctx on success.
556 g_client_transport = acip_tcp_transport_create("client", g_sockfd, NULL);
557 if (!g_client_transport) {
558 log_error("Failed to create TCP ACIP transport");
559 close_socket(g_sockfd);
560 g_sockfd = INVALID_SOCKET_VALUE;
561 return -1;
562 }
563 log_debug("CLIENT_CONNECT: Created TCP ACIP transport (crypto pending handshake)");
564
565 // Perform crypto handshake if encryption is enabled
566 log_debug("CLIENT_CONNECT: Calling client_crypto_handshake()");
567 int handshake_result = client_crypto_handshake(g_client_transport);
568 if (handshake_result != 0) {
569 log_error("Crypto handshake failed");
570 log_debug("CLIENT_CONNECT: client_crypto_handshake() failed with code %d", handshake_result);
571 acip_transport_destroy(g_client_transport);
572 g_client_transport = NULL;
573 close_socket(g_sockfd);
574 g_sockfd = INVALID_SOCKET_VALUE;
576 "Crypto handshake failed with server - this usually indicates a protocol mismatch or network issue");
577 }
578 log_debug("CLIENT_CONNECT: client_crypto_handshake() succeeded");
579
580 // Turn OFF terminal logging when successfully connected to server
581 // First connection - we'll disable logging after main.c shows the "Connected successfully" message
582 if (!GET_OPTION(snapshot_mode)) {
583 log_debug("Connected to server - terminal logging will be disabled after initial setup");
584 } else {
585 log_debug("Connected to server - terminal logging kept enabled for snapshot mode");
586 }
587
588 // Configure socket options for optimal performance
589 if (socket_set_keepalive(g_sockfd, true) < 0) {
590 log_warn("Failed to set socket keepalive: %s", network_error_string());
591 }
592
593 // Configure socket buffers and TCP_NODELAY for optimal performance
594 asciichat_error_t sock_config_result = socket_configure_buffers(g_sockfd);
595 if (sock_config_result != ASCIICHAT_OK) {
596 log_warn("Failed to configure socket: %s", network_error_string());
597 }
598
599 // Send initial terminal capabilities to server (this may generate debug logs)
602 if (result < 0) {
603 log_error("Failed to send initial capabilities to server: %s", network_error_string());
604 close_socket(g_sockfd);
605 g_sockfd = INVALID_SOCKET_VALUE;
606 return -1;
607 }
608
609 // Now disable terminal logging after capabilities are sent (for reconnections)
610 if (!GET_OPTION(snapshot_mode) && has_ever_connected) {
612 log_debug("Reconnected to server - terminal logging disabled to prevent interference with ASCII display");
613 }
614
615 // Send client join packet for multi-user support
616 uint32_t my_capabilities = CLIENT_CAP_VIDEO; // Basic video capability
617 log_debug("GET_OPTION(audio_enabled) = %d (sending CLIENT_JOIN)", GET_OPTION(audio_enabled));
618 if (GET_OPTION(audio_enabled)) {
619 log_debug("Adding CLIENT_CAP_AUDIO to capabilities");
620 my_capabilities |= CLIENT_CAP_AUDIO;
621 }
622 if (GET_OPTION(color_mode) != COLOR_MODE_NONE) {
623 my_capabilities |= CLIENT_CAP_COLOR;
624 }
625 if (GET_OPTION(stretch)) {
626 my_capabilities |= CLIENT_CAP_STRETCH;
627 }
628
629 // Generate display name from username + PID
630 const char *display_name = platform_get_username();
631
632 char my_display_name[MAX_DISPLAY_NAME_LEN];
633 int pid = getpid();
634 SAFE_SNPRINTF(my_display_name, sizeof(my_display_name), "%s-%d", display_name, pid);
635
636 if (threaded_send_client_join_packet(my_display_name, my_capabilities) < 0) {
637 log_error("Failed to send client join packet: %s", network_error_string());
638 close_socket(g_sockfd);
639 g_sockfd = INVALID_SOCKET_VALUE;
640 return -1;
641 }
642
643 // Connection already marked as active after socket creation
644
645 return 0;
646}
asciichat_error_t connection_endpoint_resolve(const char *input, uint16_t default_port, connection_endpoint_t *endpoint_out)
@ CONNECTION_ENDPOINT_WEBSOCKET
asciichat_error_t connection_factory_open(const char *name, const char *input, uint16_t default_port, crypto_context_t *crypto_ctx, acip_transport_t **transport_out, connection_endpoint_t *endpoint_out)
#define NET_TO_HOST_U16(val)
Definition endian.h:111
asciichat_error_t threaded_send_terminal_size_with_auto_detect(unsigned short width, unsigned short height)
Thread-safe terminal size packet transmission with auto-detection.
int threaded_send_client_join_packet(const char *display_name, uint32_t capabilities)
Thread-safe client join packet transmission.
void server_connection_set_ip(const char *ip)
Set the server IP address.
void server_connection_set_port(int port)
Set the server port.
const crypto_context_t * crypto_client_get_context(void)
Get crypto context for encryption/decryption.
int client_crypto_handshake(acip_transport_t *transport)
Perform crypto handshake with server.
int client_crypto_init(void)
Initialize client crypto handshake.
bool crypto_client_is_ready(void)
Check if crypto handshake is ready.
unsigned short uint16_t
Definition common.h:57
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:414
#define SAFE_SNPRINTF(buffer, buffer_size,...)
Definition common.h:492
#define FATAL(code,...)
Exit with error code and custom message, with stack trace in debug builds.
Definition common.h:158
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ ERROR_CRYPTO_HANDSHAKE
Definition error_codes.h:99
@ ASCIICHAT_OK
Definition error_codes.h:51
#define MAX_DISPLAY_NAME_LEN
Maximum display name length in characters.
Definition limits.h:20
#define log_error(...)
Log an ERROR message.
Definition log/log.h:587
#define log_info(...)
Log an INFO message.
Definition log/log.h:561
#define CLIENT_CAP_STRETCH
Client can stretch frames to fill terminal.
Definition packet.h:926
#define CLIENT_CAP_COLOR
Client supports color rendering.
Definition packet.h:925
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.
#define CLIENT_CAP_AUDIO
Client can send/receive audio.
Definition packet.h:924
#define CLIENT_CAP_VIDEO
Client can send/receive video.
Definition packet.h:923
#define COLOR_MODE_NONE
Monochrome mode.
int socket_set_keepalive(socket_t sock, bool keepalive)
Set SO_KEEPALIVE socket option.
void platform_sleep_us(unsigned int us)
High-precision sleep function with microsecond precision.
const char * platform_get_username(void)
Get the current username.
#define close_socket
bool should_exit(void)
Definition misc.c:131
unsigned short int terminal_get_effective_width(void)
Get effective terminal width with fallback priority.
unsigned short int terminal_get_effective_height(void)
Get effective terminal height with fallback priority.
@ CONNECTION_ERROR_AUTH_FAILED
Authentication failure (no retry)
socket_t(* get_socket)(acip_transport_t *transport)
Get underlying socket (if applicable)
Definition transport.h:175
const acip_transport_methods_t * methods
Method table (virtual functions)
Definition transport.h:215
connection_endpoint_protocol_t protocol
Cryptographic context structure.
acip_transport_t * acip_tcp_transport_create(const char *name, socket_t sockfd, crypto_context_t *crypto_ctx)
Create TCP transport from existing socket.

References acip_tcp_transport_create(), acip_transport_destroy(), ASCIICHAT_OK, atomic_store_bool(), CLIENT_CAP_AUDIO, CLIENT_CAP_COLOR, CLIENT_CAP_STRETCH, CLIENT_CAP_VIDEO, client_crypto_handshake(), client_crypto_init(), close_socket, COLOR_MODE_NONE, connection_endpoint_resolve(), CONNECTION_ENDPOINT_WEBSOCKET, CONNECTION_ERROR_AUTH_FAILED, connection_factory_open(), crypto_client_get_context(), crypto_client_is_ready(), ERROR_CRYPTO_HANDSHAKE, FATAL, GET_OPTION, acip_transport_methods_t::get_socket, connection_endpoint_t::host, connection_endpoint_t::input, INVALID_SOCKET_VALUE, log_debug, log_error, log_info, log_set_terminal_output(), log_warn, MAX_DISPLAY_NAME_LEN, acip_transport::methods, NET_TO_HOST_U16, network_error_string(), platform_get_username(), platform_sleep_us(), connection_endpoint_t::port, connection_endpoint_t::protocol, SAFE_SNPRINTF, SAFE_STRNCPY, server_connection_set_ip(), server_connection_set_port(), should_exit(), socket_configure_buffers(), socket_set_keepalive(), terminal_get_effective_height(), terminal_get_effective_width(), threaded_send_client_join_packet(), and threaded_send_terminal_size_with_auto_detect().

◆ server_connection_get_client_id()

uint32_t server_connection_get_client_id ( )

#include <server.c>

Get client ID assigned by server.

Returns
Client ID (based on local port) or 0 if not connected
Client ID or 0 if not connected

Definition at line 737 of file src/client/server.c.

737 {
738 return g_my_client_id;
739}

◆ server_connection_get_ip()

const char * server_connection_get_ip ( )

#include <server.c>

Get resolved server IP address.

Returns the server's IP address that was resolved during connection establishment. Used for known_hosts verification and logging.

Returns
Server IP address string (IPv4 or IPv6), or empty string if not connected
Server IP address string (IPv4 or IPv6)

Definition at line 751 of file src/client/server.c.

751 {
752 return g_server_ip;
753}

Referenced by client_crypto_init().

◆ server_connection_get_port()

int server_connection_get_port ( void  )

#include <server.c>

Get the server port.

Returns the currently configured server port.

Returns
Server port number (0 if not set)
Server port number

Definition at line 797 of file src/client/server.c.

797 {
798 return g_server_port;
799}

Referenced by client_crypto_init().

◆ server_connection_get_socket()

socket_t server_connection_get_socket ( )

#include <server.c>

Get current socket file descriptor.

Returns
Socket file descriptor or INVALID_SOCKET_VALUE if disconnected
Socket FD or INVALID_SOCKET_VALUE if disconnected

Definition at line 668 of file src/client/server.c.

668 {
669 return g_sockfd;
670}

◆ server_connection_get_transport()

struct acip_transport * server_connection_get_transport ( void  )

#include <server.c>

Get ACIP transport instance.

Returns
Transport instance or NULL if not connected

Definition at line 679 of file src/client/server.c.

679 {
680 return g_client_transport;
681}

Referenced by crypto_client_initiate_rekey(), crypto_client_send_rekey_complete(), crypto_client_send_rekey_response(), protocol_start_connection(), protocol_stop_connection(), threaded_send_image_frame(), and threaded_send_image_frame_h265().

◆ server_connection_init()

int server_connection_init ( )

#include <server.c>

Initialize the server connection management subsystem.

Initialize server connection management subsystem.

Sets up the send mutex and initializes connection state variables. Must be called once during client startup before any connection attempts.

Returns
0 on success, non-zero on failure
0 on success, negative on error

Definition at line 311 of file src/client/server.c.

311 {
312 // Initialize mutex for thread-safe packet sending
313 if (mutex_init(&g_send_mutex, "send") != 0) {
314 log_error("Failed to initialize send mutex");
315 return -1;
316 }
317
318 // Register connection atomics for debug sync state monitoring (one-time registration)
319 static bool connection_atomics_registered = false;
320 if (!connection_atomics_registered) {
321 NAMED_REGISTER_ATOMIC(&g_connection_active, "server_connection_active_flag", NULL);
322 NAMED_REGISTER_ATOMIC(&g_connection_lost, "server_connection_lost_flag", NULL);
323 NAMED_REGISTER_ATOMIC(&g_should_reconnect, "should_attempt_reconnection_flag", NULL);
324 connection_atomics_registered = true;
325 }
326
327 // Initialize connection state
328 g_sockfd = INVALID_SOCKET_VALUE;
329 g_client_transport = NULL;
330 atomic_store_bool(&g_connection_active, false);
331 atomic_store_bool(&g_connection_lost, false);
332 atomic_store_bool(&g_should_reconnect, false);
333 g_my_client_id = 0;
334
335 return 0;
336}
#define NAMED_REGISTER_ATOMIC(a, name, parent_ptr)
Register an atomic_t 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_error, mutex_init(), and NAMED_REGISTER_ATOMIC.

◆ server_connection_is_active()

bool server_connection_is_active ( )

#include <server.c>

Check if server connection is currently active.

Returns
true if connection is active, false otherwise

Definition at line 655 of file src/client/server.c.

655 {
656 // For TCP: check socket validity
657 // For WebRTC: socket is INVALID_SOCKET_VALUE but transport exists
658 return atomic_load_bool(&g_connection_active) && (g_sockfd != INVALID_SOCKET_VALUE || g_client_transport != NULL);
659}
bool atomic_load_bool(atomic_t *a)
Atomically load a boolean value.
Definition atomic.c:169

References atomic_load_bool(), and INVALID_SOCKET_VALUE.

Referenced by protocol_start_connection(), protocol_stop_connection(), threaded_send_image_frame(), and threaded_send_image_frame_h265().

◆ server_connection_is_lost()

bool server_connection_is_lost ( )

#include <server.c>

Check if connection loss has been detected.

Check if connection loss was detected.

Returns
true if connection loss was flagged, false otherwise
true if connection lost, false otherwise

Definition at line 946 of file src/client/server.c.

946 {
947 return atomic_load_bool(&g_connection_lost);
948}

References atomic_load_bool().

Referenced by protocol_connection_lost().

◆ server_connection_lost()

void server_connection_lost ( )

#include <server.c>

Signal that connection has been lost.

Called by other modules (typically protocol handlers) when they detect connection failure. Triggers reconnection logic in main loop.

Definition at line 930 of file src/client/server.c.

930 {
931 atomic_store_bool(&g_connection_lost, true);
932 atomic_store_bool(&g_connection_active, false);
933
934 // Don't re-enable terminal logging here - let the splash screen handle it
935 // The reconnection splash will capture and display logs properly
937}
void display_full_reset()
Perform full display reset.

References atomic_store_bool(), and display_full_reset().

Referenced by threaded_send_audio_batch_packet(), threaded_send_audio_opus(), threaded_send_audio_opus_batch(), threaded_send_image_frame(), threaded_send_image_frame_h265(), and threaded_send_packet().

◆ server_connection_set_ip()

void server_connection_set_ip ( const char *  ip)

#include <server.c>

Set the server IP address.

Updates the global server IP address for the active transport connection.

Parameters
ipServer IP address string
ipServer IP address string

Definition at line 764 of file src/client/server.c.

764 {
765 if (ip) {
766 SAFE_STRNCPY(g_server_ip, ip, sizeof(g_server_ip));
767 log_debug("Server IP set to: %s", g_server_ip);
768 } else {
769 g_server_ip[0] = '\0';
770 log_debug("Server IP cleared");
771 }
772}

References log_debug, and SAFE_STRNCPY.

Referenced by client_main(), connection_attempt_tcp(), and server_connection_establish().

◆ server_connection_set_port()

void server_connection_set_port ( int  port)

#include <server.c>

Set the server port.

Updates the global server port for the active transport connection.

Parameters
portServer port number
portServer port number

Definition at line 783 of file src/client/server.c.

783 {
784 g_server_port = port;
785 log_debug("Server port set to: %d", g_server_port);
786}

References log_debug.

Referenced by client_main(), connection_attempt_tcp(), and server_connection_establish().

◆ server_connection_set_transport()

void server_connection_set_transport ( acip_transport_t *  transport)

#include <server.c>

Set ACIP transport instance from connection fallback.

Used to integrate the transport from the 3-stage connection fallback orchestrator (TCP β†’ STUN β†’ TURN) into the server connection management layer.

Parameters
transportTransport instance created by connection_attempt_with_fallback()
transportTransport instance created by connection_attempt_with_fallback()

Used to integrate the transport from the 3-stage connection fallback orchestrator (TCP β†’ STUN β†’ TURN) into the server connection management layer.

Definition at line 693 of file src/client/server.c.

693 {
694 log_debug("[TRANSPORT_LIFECYCLE] server_connection_set_transport() called with transport=%p", (void *)transport);
695
696 // Clean up any existing transport
697 if (g_client_transport) {
698 log_warn("[TRANSPORT_LIFECYCLE] Replacing existing transport=%p with new transport=%p (potential premature "
699 "destruction?)",
700 (void *)g_client_transport, (void *)transport);
701 log_debug("[TRANSPORT_LIFECYCLE] OLD transport is_connected=%s before destruction",
702 acip_transport_is_connected(g_client_transport) ? "true" : "false");
703 acip_transport_destroy(g_client_transport);
704 log_debug("[TRANSPORT_LIFECYCLE] OLD transport destroyed");
705 }
706
707 log_debug("[TRANSPORT_LIFECYCLE] Setting g_client_transport to %p", (void *)transport);
708 g_client_transport = transport;
709
710 // Mark connection as active when transport is set
711 if (transport) {
712 log_debug("[TRANSPORT_LIFECYCLE] Transport is non-NULL, extracting socket...");
713 g_sockfd = acip_transport_get_socket(transport);
714 log_debug("[TRANSPORT_LIFECYCLE] Socket extracted: %d from transport=%p", (int)g_sockfd, (void *)transport);
715
716 atomic_store_bool(&g_connection_active, true);
717 atomic_store_bool(&g_connection_lost, false); // Reset lost flag for new connection
718 log_debug("[TRANSPORT_LIFECYCLE] Server connection transport set and marked active (transport=%p, sockfd=%d, "
719 "is_connected=%s)",
720 (void *)transport, (int)g_sockfd, acip_transport_is_connected(transport) ? "true" : "false");
721 } else {
722 g_sockfd = INVALID_SOCKET_VALUE;
723 atomic_store_bool(&g_connection_active, false);
724 log_debug("[TRANSPORT_LIFECYCLE] Server connection transport cleared and marked inactive");
725 }
726
727 log_debug("[TRANSPORT_LIFECYCLE] server_connection_set_transport() completed");
728}

References acip_transport_destroy(), atomic_store_bool(), INVALID_SOCKET_VALUE, log_debug, and log_warn.

◆ server_connection_shutdown()

void server_connection_shutdown ( )

#include <server.c>

Emergency connection shutdown for signal handlers.

Emergency shutdown for signal handlers.

Performs immediate connection shutdown without waiting for graceful close procedures. Uses socket shutdown to interrupt any blocking recv() operations in other threads.

Definition at line 894 of file src/client/server.c.

894 {
895 // NOTE: This function may be called from:
896 // - Signal handlers on Unix (async-signal-safe context)
897 // - SetConsoleCtrlHandler callback thread on Windows (separate thread context)
898 // Only use atomic operations and simple system calls - NO mutex locks, NO malloc, NO logging.
899
900 atomic_store_bool(&g_connection_active, false);
901 atomic_store_bool(&g_connection_lost, true);
902
903 if (g_sockfd != INVALID_SOCKET_VALUE) {
904 // Only shutdown() the socket to interrupt blocking recv()/send() operations.
905 // Do NOT close() here - on Windows, closing the socket while another thread
906 // is using it is undefined behavior and can cause STATUS_STACK_BUFFER_OVERRUN.
907 // The actual socket close happens in server_connection_close() which is called
908 // from the main thread after worker threads have been joined.
909 socket_shutdown(g_sockfd, SHUT_RDWR);
910 }
911
912 // For WebSocket transport: interrupt blocking recv() calls
913 // The recv() checks is_connected before waiting for data. Setting this to false
914 // will cause any blocked recv() to return immediately with CONNECTION_CLOSED.
915 // We use a function that's async-signal-safe (no mutexes) to do this.
916 server_connection_interrupt_websocket_recv();
917
918 // DO NOT call log_set_terminal_output() here - it uses mutex which is NOT async-signal-safe.
919 // The normal cleanup path in shutdown_client() will handle logging state.
920}

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

Referenced by client_main(), and protocol_stop_connection().

◆ server_send_audio()

int server_send_audio ( const float *  samples,
int  num_samples 
)

#include <server.h>

Send audio data to server.

Parameters
samplesAudio sample buffer
num_samplesNumber of samples
Returns
0 on success, negative on error

◆ server_send_audio_batch()

int server_send_audio_batch ( const float *  samples,
int  num_samples,
int  batch_count 
)

#include <server.h>

Send batched audio data to server.

Parameters
samplesBatched audio samples
num_samplesTotal sample count
batch_countNumber of packets in batch
Returns
0 on success, negative on error

◆ server_send_packet()

int server_send_packet ( packet_type_t  type,
const void *  data,
size_t  len 
)

#include <server.h>

Send general packet to server.

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

◆ server_send_ping()

int server_send_ping ( )

#include <server.h>

Send ping keepalive packet.

Returns
0 on success, negative on error

◆ server_send_pong()

int server_send_pong ( )

#include <server.h>

Send pong response packet.

Returns
0 on success, negative on error

◆ server_send_stream_start()

int server_send_stream_start ( uint32_t  stream_type)

#include <server.h>

Send stream start notification.

Parameters
stream_typeType of stream (audio/video)
Returns
0 on success, negative on error

◆ server_send_stream_stop()

int server_send_stream_stop ( uint32_t  stream_type)

#include <server.h>

Send stream stop notification.

Parameters
stream_typeType of stream (audio/video)
Returns
0 on success, negative on error

◆ server_send_terminal_capabilities()

int server_send_terminal_capabilities ( unsigned short  width,
unsigned short  height 
)

#include <server.h>

Send terminal capabilities to server.

Parameters
widthTerminal width in characters
heightTerminal height in characters
Returns
0 on success, negative on error

◆ threaded_send_audio_batch_packet()

int threaded_send_audio_batch_packet ( const float *  samples,
int  num_samples,
int  batch_count 
)

#include <server.c>

Thread-safe batched audio packet transmission.

Sends a batched audio packet to the server with proper mutex protection and connection state checking. Automatically handles encryption if crypto is ready.

Parameters
samplesAudio sample buffer containing batched samples
num_samplesTotal number of samples in the batch
batch_countNumber of audio chunks in this batch
Returns
0 on success, negative on error
Parameters
samplesAudio sample buffer containing batched samples
num_samplesTotal number of samples in the batch
batch_countNumber of audio chunks in this batch
Returns
0 on success, negative on error

Definition at line 1034 of file src/client/server.c.

1034 {
1035 // Lock mutex for entire send operation to prevent concurrent socket writes
1036 mutex_lock(&g_send_mutex);
1037
1038 // Check connection status and get transport reference
1039 if (!atomic_load_bool(&g_connection_active) || !g_client_transport) {
1040 mutex_unlock(&g_send_mutex);
1041 return -1;
1042 }
1043
1044 // Get transport reference - transport has its own internal synchronization
1045 acip_transport_t *transport = g_client_transport;
1046
1047 // Network I/O happens while holding mutex to prevent concurrent socket writes
1048 asciichat_error_t result = acip_send_audio_batch(transport, samples, (uint32_t)num_samples, (uint32_t)batch_count);
1049
1050 // Unlock after send completes
1051 mutex_unlock(&g_send_mutex);
1052
1053 // If send failed due to network error, signal connection loss
1054 if (result != ASCIICHAT_OK) {
1056 return -1;
1057 }
1058
1059 return 0;
1060}
asciichat_error_t acip_send_audio_batch(acip_transport_t *transport, const float *samples, uint32_t num_samples, uint32_t batch_count)
Send audio batch packet.
Definition send.c:180
Transport instance structure.
Definition transport.h:214

References acip_send_audio_batch(), ASCIICHAT_OK, atomic_load_bool(), mutex_lock, mutex_unlock, and server_connection_lost().

◆ threaded_send_audio_opus()

asciichat_error_t threaded_send_audio_opus ( const uint8_t *  opus_data,
size_t  opus_size,
int  sample_rate,
int  frame_duration 
)

#include <server.c>

Thread-safe Opus audio frame transmission.

Sends a single Opus-encoded audio frame to the server with proper synchronization and encryption support.

Parameters
opus_dataOpus-encoded audio data
opus_sizeSize of encoded frame
sample_rateSample rate in Hz
frame_durationFrame duration in milliseconds
Returns
0 on success, negative on error

Sends a single Opus-encoded audio frame to the server with proper synchronization and encryption support.

Parameters
opus_dataOpus-encoded audio data
opus_sizeSize of encoded frame
sample_rateSample rate in Hz
frame_durationFrame duration in milliseconds
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 1076 of file src/client/server.c.

1077 {
1078 // Get transport reference while holding mutex (brief lock)
1079 mutex_lock(&g_send_mutex);
1080
1081 // Check connection status and get transport reference
1082 if (!atomic_load_bool(&g_connection_active) || !g_client_transport) {
1083 mutex_unlock(&g_send_mutex);
1084 return SET_ERRNO(ERROR_NETWORK, "Connection not active or transport unavailable");
1085 }
1086
1087 // Get transport reference - transport has its own internal synchronization
1088 acip_transport_t *transport = g_client_transport;
1089 mutex_unlock(&g_send_mutex);
1090
1091 // Build Opus packet with header (outside mutex - no blocking I/O yet)
1092 size_t header_size = 16; // sample_rate (4), frame_duration (4), reserved (8)
1093 size_t total_size = header_size + opus_size;
1094 void *packet_data = buffer_pool_alloc(NULL, total_size);
1095 if (!packet_data) {
1096 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer for Opus packet: %zu bytes", total_size);
1097 }
1098
1099 // Write header in network byte order
1100 uint8_t *buf = (uint8_t *)packet_data;
1101 uint32_t sr = HOST_TO_NET_U32((uint32_t)sample_rate);
1102 uint32_t fd = HOST_TO_NET_U32((uint32_t)frame_duration);
1103 memcpy(buf, &sr, 4);
1104 memcpy(buf + 4, &fd, 4);
1105 memset(buf + 8, 0, 8); // Reserved
1106
1107 // Copy Opus data
1108 memcpy(buf + header_size, opus_data, opus_size);
1109
1110 // Network I/O happens OUTSIDE the mutex to prevent deadlock on TCP buffer full
1111 asciichat_error_t result =
1112 packet_send_via_transport(transport, PACKET_TYPE_AUDIO_OPUS_BATCH, packet_data, total_size, 0);
1113
1114 // Clean up
1115 buffer_pool_free(NULL, packet_data, total_size);
1116
1117 // If send failed due to network error, signal connection loss
1118 if (result != ASCIICHAT_OK) {
1120 return result;
1121 }
1122
1123 return ASCIICHAT_OK;
1124}
#define HOST_TO_NET_U32(val)
Definition endian.h:66
void buffer_pool_free(buffer_pool_t *pool, const void *data, size_t size)
Free a buffer back to the pool (lock-free)
void * buffer_pool_alloc(buffer_pool_t *pool, size_t size)
Allocate a buffer from the pool (lock-free fast path)
unsigned char uint8_t
Definition common.h:56
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
@ ERROR_NETWORK
Definition error_codes.h:77
@ ERROR_MEMORY
Definition error_codes.h:56
@ PACKET_TYPE_AUDIO_OPUS_BATCH
Batched Opus-encoded audio frames.
Definition packet.h:374
asciichat_error_t packet_send_via_transport(acip_transport_t *transport, packet_type_t type, const void *payload, size_t payload_len, uint32_t client_id)
Send packet via transport with proper header (exported for generic wrappers)
Definition send.c:41

References ASCIICHAT_OK, atomic_load_bool(), buffer_pool_alloc(), buffer_pool_free(), ERROR_MEMORY, ERROR_NETWORK, HOST_TO_NET_U32, mutex_lock, mutex_unlock, packet_send_via_transport(), PACKET_TYPE_AUDIO_OPUS_BATCH, server_connection_lost(), and SET_ERRNO.

◆ threaded_send_audio_opus_batch()

asciichat_error_t threaded_send_audio_opus_batch ( const uint8_t *  opus_data,
size_t  opus_size,
const uint16_t *  frame_sizes,
int  frame_count 
)

#include <server.c>

Thread-safe Opus audio batch packet transmission.

Sends a batch of Opus-encoded audio frames to the server with proper synchronization and encryption support.

Parameters
opus_dataOpus-encoded audio data (multiple frames concatenated)
opus_sizeTotal size of Opus data in bytes
frame_sizesArray of individual frame sizes (variable-length frames)
frame_countNumber of Opus frames in the batch
Returns
0 on success, negative on error

Sends a batch of Opus-encoded audio frames to the server with proper synchronization and encryption support.

Parameters
opus_dataOpus-encoded audio data (multiple frames concatenated)
opus_sizeTotal size of Opus data in bytes
frame_sizesArray of individual frame sizes (variable-length frames)
frame_countNumber of Opus frames in the batch
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 1140 of file src/client/server.c.

1141 {
1142 // Lock mutex for entire send operation to prevent concurrent socket writes
1143 mutex_lock(&g_send_mutex);
1144
1145 // Check connection status and get transport reference
1146 if (!atomic_load_bool(&g_connection_active) || !g_client_transport) {
1147 mutex_unlock(&g_send_mutex);
1148 return SET_ERRNO(ERROR_NETWORK, "Connection not active or transport unavailable");
1149 }
1150
1151 // Get transport reference - transport has its own internal synchronization
1152 acip_transport_t *transport = g_client_transport;
1153
1154 // Network I/O happens while holding mutex to prevent concurrent socket writes
1155 // Opus uses 20ms frames at 48kHz (960 samples = 20ms)
1156 asciichat_error_t result =
1157 acip_send_audio_opus_batch(transport, opus_data, opus_size, frame_sizes, frame_count, 48000, 20);
1158
1159 // Unlock after send completes
1160 mutex_unlock(&g_send_mutex);
1161
1162 // If send failed due to network error, signal connection loss
1163 if (result != ASCIICHAT_OK) {
1165 return result;
1166 }
1167
1168 return ASCIICHAT_OK;
1169}
asciichat_error_t acip_send_audio_opus_batch(acip_transport_t *transport, const void *opus_data, size_t opus_len, const uint16_t *frame_sizes, uint32_t frame_count, uint32_t sample_rate, uint32_t frame_duration)
Send batched Opus-encoded audio frames.
Definition send.c:223

References acip_send_audio_opus_batch(), ASCIICHAT_OK, atomic_load_bool(), ERROR_NETWORK, mutex_lock, mutex_unlock, server_connection_lost(), and SET_ERRNO.

◆ threaded_send_client_join_packet()

int threaded_send_client_join_packet ( const char *  display_name,
uint32_t  capabilities 
)

#include <server.c>

Thread-safe client join packet transmission.

Sends client join packet with display name and capabilities to the server.

Parameters
display_nameClient display name
capabilitiesClient capability flags
Returns
0 on success, negative on error
Parameters
display_nameClient display name
capabilitiesClient capability flags
Returns
0 on success, negative on error

Definition at line 1466 of file src/client/server.c.

1466 {
1467 // Connection and transport availability is checked by threaded_send_packet()
1468
1469 // Build CLIENT_JOIN packet locally
1470 client_info_packet_t join_packet;
1471 SAFE_MEMSET(&join_packet, sizeof(join_packet), 0, sizeof(join_packet));
1472 join_packet.client_id = HOST_TO_NET_U32(0); // Will be assigned by server
1473 SAFE_SNPRINTF(join_packet.display_name, MAX_DISPLAY_NAME_LEN, "%s", display_name ? display_name : "Unknown");
1474 join_packet.capabilities = HOST_TO_NET_U32(capabilities);
1475
1476 // Use threaded_send_packet() which handles encryption
1477 int send_result = threaded_send_packet(PACKET_TYPE_CLIENT_JOIN, &join_packet, sizeof(join_packet));
1478 if (send_result == 0) {
1479 mutex_lock(&g_send_mutex);
1480 bool active = atomic_load_u64(&g_connection_active);
1481 socket_t socket_snapshot = g_sockfd;
1482 const crypto_context_t *crypto_ctx = crypto_client_is_ready() ? crypto_client_get_context() : NULL;
1483 if (active && socket_snapshot != INVALID_SOCKET_VALUE) {
1484 (void)log_network_message(
1485 socket_snapshot, (const struct crypto_context_t *)crypto_ctx, LOG_INFO, REMOTE_LOG_DIRECTION_CLIENT_TO_SERVER,
1486 "CLIENT_JOIN sent (display=\"%s\", capabilities=0x%x)", join_packet.display_name, capabilities);
1487 }
1488 mutex_unlock(&g_send_mutex);
1489 }
1490 return send_result;
1491}
uint64_t atomic_load_u64(atomic_t *a)
Atomically load a uint64_t value.
Definition atomic.c:233
asciichat_error_t threaded_send_packet(packet_type_t type, const void *data, size_t len)
Thread-safe packet transmission.
#define SAFE_MEMSET(dest, dest_size, ch, count)
Definition common.h:469
asciichat_error_t log_network_message(socket_t sockfd, const struct crypto_context_t *crypto_ctx, log_level_t level, remote_log_direction_t direction, const char *fmt,...)
Send a formatted log message over the network.
Definition log/log.c:1550
@ REMOTE_LOG_DIRECTION_CLIENT_TO_SERVER
Definition network/log.h:23
@ PACKET_TYPE_CLIENT_JOIN
Client announces capability to send media.
Definition packet.h:387
Client information packet structure.
Definition packet.h:653
char display_name[32]
User display name (null-terminated, max MAX_DISPLAY_NAME_LEN bytes)
Definition packet.h:657
uint32_t client_id
Unique client identifier (1-9, 0 = server)
Definition packet.h:655
uint32_t capabilities
Client capabilities bitmask (CLIENT_CAP_VIDEO | CLIENT_CAP_AUDIO | CLIENT_CAP_COLOR | CLIENT_CAP_STRE...
Definition packet.h:660
#define LOG_INFO
Definition types.h:40

References atomic_load_u64(), client_info_packet_t::capabilities, client_info_packet_t::client_id, crypto_client_get_context(), crypto_client_is_ready(), client_info_packet_t::display_name, HOST_TO_NET_U32, INVALID_SOCKET_VALUE, LOG_INFO, log_network_message(), MAX_DISPLAY_NAME_LEN, mutex_lock, mutex_unlock, PACKET_TYPE_CLIENT_JOIN, REMOTE_LOG_DIRECTION_CLIENT_TO_SERVER, SAFE_MEMSET, SAFE_SNPRINTF, and threaded_send_packet().

Referenced by server_connection_establish().

◆ threaded_send_image_frame()

asciichat_error_t threaded_send_image_frame ( const void *  pixel_data,
uint32_t  width,
uint32_t  height,
uint32_t  pixel_format 
)

#include <server.c>

Thread-safe image frame transmission.

Sends image frames with serialization via g_send_mutex to prevent race conditions when multiple threads write to the same TCP socket.

Parameters
pixel_dataPixel data buffer
widthFrame width in pixels
heightFrame height in pixels
pixel_formatPixel format (1=RGB24)
Returns
ASCIICHAT_OK on success, error code on failure

Sends image frames with serialization via mutex to prevent race conditions when multiple threads write to the same TCP socket.

Parameters
pixel_dataPixel data buffer (RGB24 format)
widthFrame width in pixels
heightFrame height in pixels
pixel_formatPixel format (1=RGB24)
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 1185 of file src/client/server.c.

1186 {
1187 if (!pixel_data) {
1188 return SET_ERRNO(ERROR_INVALID_PARAM, "Pixel data is NULL");
1189 }
1190
1191 mutex_lock(&g_send_mutex);
1192
1193 // Get transport reference - may be NULL if connection was lost
1195 if (!transport || !server_connection_is_active()) {
1196 mutex_unlock(&g_send_mutex);
1197 return SET_ERRNO(ERROR_NETWORK, "Transport unavailable");
1198 }
1199
1200 // Send image frame with socket lock held
1201 asciichat_error_t result = acip_send_image_frame(transport, pixel_data, width, height, pixel_format);
1202
1203 // Unlock after send completes
1204 mutex_unlock(&g_send_mutex);
1205
1206 // If send failed due to network error, signal connection loss
1207 if (result != ASCIICHAT_OK) {
1209 return result;
1210 }
1211
1212 return ASCIICHAT_OK;
1213}
acip_transport_t * server_connection_get_transport(void)
Get ACIP transport instance.
@ ERROR_INVALID_PARAM
asciichat_error_t acip_send_image_frame(acip_transport_t *transport, const void *pixel_data, uint32_t width, uint32_t height, uint32_t pixel_format)
Send image frame to server (client β†’ server)

References acip_send_image_frame(), ASCIICHAT_OK, ERROR_INVALID_PARAM, ERROR_NETWORK, mutex_lock, mutex_unlock, server_connection_get_transport(), server_connection_is_active(), server_connection_lost(), and SET_ERRNO.

◆ threaded_send_image_frame_h265()

asciichat_error_t threaded_send_image_frame_h265 ( const void *  pixel_data,
uint32_t  width,
uint32_t  height 
)

#include <server.c>

Send H.265-encoded video frame to server (thread-safe)

Encodes RGB frame using H.265 codec before transmission. Much more bandwidth-efficient than raw pixel transmission. Uses mutex to prevent interleaving with other packet sends.

Parameters
pixel_dataRGB pixel data from camera
widthFrame width in pixels
heightFrame height in pixels
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 1229 of file src/client/server.c.

1229 {
1230 if (!pixel_data) {
1231 return SET_ERRNO(ERROR_INVALID_PARAM, "Pixel data is NULL");
1232 }
1233
1234 // Get encoder - may be NULL if not initialized
1235 h265_encoder_t *encoder = g_h265_encoder;
1236 if (!encoder) {
1237 return SET_ERRNO(ERROR_INVALID_STATE, "H.265 encoder not initialized");
1238 }
1239
1240 // Allocate buffer and encode frame WITHOUT holding the send mutex.
1241 // This avoids long critical section that can cause deadlocks with named registry operations.
1242 log_info("[H265_SEND_FRAME_1] Allocating buffer and encoding frame (no mutex held)");
1243 size_t output_buffer_size = 5 + (width * height * 2);
1244 uint8_t *output_buffer = buffer_pool_alloc(NULL, output_buffer_size);
1245 if (!output_buffer) {
1246 log_error("Failed to allocate output buffer for frame");
1247 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate H.265 output buffer: %zu bytes", output_buffer_size);
1248 }
1249
1250 // Encode frame
1251 size_t encoded_size = output_buffer_size - 5;
1252 asciichat_error_t encode_result = h265_encode(encoder, (uint16_t)width, (uint16_t)height, (const uint8_t *)pixel_data,
1253 output_buffer + 5, &encoded_size);
1254 log_info("[H265_SEND_FRAME_2] βœ… Frame encoded, encoded_size=%zu, result=%d", encoded_size, encode_result);
1255
1256 if (encode_result != ASCIICHAT_OK) {
1257 log_error("H.265 encoding failed: %s", asciichat_error_string(encode_result));
1258 buffer_pool_free(NULL, output_buffer, output_buffer_size);
1259 return encode_result;
1260 }
1261
1262 // Prepare packet header: [flags:u8][width:u16][height:u16][h265_data...]
1263 output_buffer[0] = 0; // flags: no flags set by default
1264 output_buffer[1] = (width >> 8) & 0xFF;
1265 output_buffer[2] = width & 0xFF;
1266 output_buffer[3] = (height >> 8) & 0xFF;
1267 output_buffer[4] = height & 0xFF;
1268
1269 size_t total_size = 5 + encoded_size;
1270
1271 // NOW acquire the send mutex just for the actual network transmission
1272 log_info("[H265_SEND_FRAME_3] About to acquire g_send_mutex for network send");
1273 mutex_lock(&g_send_mutex);
1274 log_info("[H265_SEND_FRAME_4] βœ… Acquired g_send_mutex, checking transport");
1275
1276 // Get transport reference - may be NULL if connection was lost
1278 if (!transport || !server_connection_is_active()) {
1279 mutex_unlock(&g_send_mutex);
1280 log_info("[H265_SEND_FRAME_5] Transport unavailable, releasing mutex");
1281 buffer_pool_free(NULL, output_buffer, output_buffer_size);
1282 return SET_ERRNO(ERROR_NETWORK, "Transport unavailable");
1283 }
1284
1285 // Send H.265 frame with socket lock held (short critical section)
1286 log_info("[H265_SEND_FRAME_6] Sending packet via transport (%zu bytes)", total_size);
1287 asciichat_error_t result =
1288 packet_send_via_transport(transport, PACKET_TYPE_IMAGE_FRAME_H265, output_buffer, total_size, 0);
1289 log_info("[H265_SEND_FRAME_7] packet_send_via_transport returned: %d", result);
1290
1291 // Unlock after send completes
1292 mutex_unlock(&g_send_mutex);
1293 log_info("[H265_SEND_FRAME_8] βœ… Released g_send_mutex");
1294
1295 buffer_pool_free(NULL, output_buffer, output_buffer_size);
1296 log_info("[H265_SEND_FRAME_9] βœ… Freed output buffer");
1297
1298 // If send failed due to network error, signal connection loss
1299 if (result != ASCIICHAT_OK) {
1301 return result;
1302 }
1303
1304 return ASCIICHAT_OK;
1305}
asciichat_error_t h265_encode(h265_encoder_t *encoder, uint16_t width, uint16_t height, const uint8_t *pixel_data, uint8_t *out_data, size_t *out_len)
void h265_encoder_t
@ ERROR_INVALID_STATE
@ PACKET_TYPE_IMAGE_FRAME_H265
H.265-encoded frame from client (HEVC/H.265 codec)
Definition packet.h:365
h265_encoder_t *volatile g_h265_encoder
Global H.265 encoder context for client video streaming.

References ASCIICHAT_OK, buffer_pool_alloc(), buffer_pool_free(), ERROR_INVALID_PARAM, ERROR_INVALID_STATE, ERROR_MEMORY, ERROR_NETWORK, g_h265_encoder, h265_encode(), log_error, log_info, mutex_lock, mutex_unlock, packet_send_via_transport(), PACKET_TYPE_IMAGE_FRAME_H265, server_connection_get_transport(), server_connection_is_active(), server_connection_lost(), and SET_ERRNO.

◆ threaded_send_packet()

asciichat_error_t threaded_send_packet ( packet_type_t  type,
const void *  data,
size_t  len 
)

#include <server.c>

Thread-safe packet transmission.

Sends a packet to the server with proper mutex protection and connection state checking. Automatically handles encryption if crypto is ready.

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

Sends a packet to the server with proper mutex protection and connection state checking. Automatically handles encryption if crypto is ready.

Parameters
typePacket type identifier
dataPacket payload
lenPayload length
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 987 of file src/client/server.c.

987 {
988 // Lock mutex for entire send operation to prevent concurrent socket writes
989 mutex_lock(&g_send_mutex);
990
991 // Check connection status and get transport reference
992 if (!atomic_load_bool(&g_connection_active) || !g_client_transport) {
993 log_debug("[TRANSPORT_LIFECYCLE] threaded_send_packet() check failed: active=%s, transport=%p",
994 atomic_load_bool(&g_connection_active) ? "true" : "false", (void *)g_client_transport);
995 mutex_unlock(&g_send_mutex);
996 return SET_ERRNO(ERROR_NETWORK, "Connection not active or transport unavailable");
997 }
998
999 // Get transport reference - transport has its own internal synchronization
1000 acip_transport_t *transport = g_client_transport;
1001 log_debug_every(LOG_RATE_SLOW, "[TRANSPORT_LIFECYCLE] threaded_send_packet() using transport=%p, is_connected=%s",
1002 (void *)transport, acip_transport_is_connected(transport) ? "true" : "false");
1003
1004 // Network I/O happens while holding mutex to prevent concurrent socket writes
1005 asciichat_error_t result = packet_send_via_transport(transport, type, data, len, 0);
1006
1007 // Unlock after send completes
1008 mutex_unlock(&g_send_mutex);
1009
1010 // If send failed due to network error, signal connection loss
1011 if (result != ASCIICHAT_OK) {
1012 log_debug("[TRANSPORT_LIFECYCLE] threaded_send_packet() send failed, calling server_connection_lost()");
1014 return result;
1015 }
1016
1017 return ASCIICHAT_OK;
1018}
#define LOG_RATE_SLOW
Log rate limit: 10 seconds (10,000,000 microseconds)
Definition log_rates.h:35
#define log_debug_every(interval_us, fmt,...)
Rate-limited DEBUG logging.
Definition log/log.h:702

References ASCIICHAT_OK, atomic_load_bool(), ERROR_NETWORK, log_debug, log_debug_every, LOG_RATE_SLOW, mutex_lock, mutex_unlock, packet_send_via_transport(), server_connection_lost(), and SET_ERRNO.

Referenced by threaded_send_client_join_packet(), threaded_send_ping_packet(), threaded_send_pong_packet(), threaded_send_stream_start_packet(), and threaded_send_terminal_size_with_auto_detect().

◆ threaded_send_ping_packet()

int threaded_send_ping_packet ( void  )

#include <server.c>

Thread-safe ping packet transmission.

Returns
0 on success, negative on error

Definition at line 1314 of file src/client/server.c.

1314 {
1315 // Use threaded_send_packet which handles encryption, mutex locking, and connection state
1316 return threaded_send_packet(PACKET_TYPE_PING, NULL, 0);
1317}
@ PACKET_TYPE_PING
Keepalive ping packet.
Definition packet.h:383

References PACKET_TYPE_PING, and threaded_send_packet().

◆ threaded_send_pong_packet()

int threaded_send_pong_packet ( void  )

#include <server.c>

Thread-safe pong packet transmission.

Returns
0 on success, negative on error

Definition at line 1326 of file src/client/server.c.

1326 {
1327 // Use threaded_send_packet which handles encryption, mutex locking, and connection state
1328 return threaded_send_packet(PACKET_TYPE_PONG, NULL, 0);
1329}
@ PACKET_TYPE_PONG
Keepalive pong response.
Definition packet.h:385

References PACKET_TYPE_PONG, and threaded_send_packet().

◆ threaded_send_stream_start_packet()

asciichat_error_t threaded_send_stream_start_packet ( uint32_t  stream_type)

#include <server.c>

Thread-safe stream start packet transmission.

Parameters
stream_typeType of stream (audio/video)
Returns
0 on success, negative on error

Definition at line 1339 of file src/client/server.c.

1339 {
1340 // Connection and transport availability is checked by threaded_send_packet()
1341
1342 // Build STREAM_START packet locally
1343 uint32_t type_data = HOST_TO_NET_U32(stream_type);
1344
1345 // Use threaded_send_packet() which handles encryption
1346 return threaded_send_packet(PACKET_TYPE_STREAM_START, &type_data, sizeof(type_data));
1347}
@ PACKET_TYPE_STREAM_START
Client requests to start sending video/audio.
Definition packet.h:391

References HOST_TO_NET_U32, PACKET_TYPE_STREAM_START, and threaded_send_packet().

Referenced by protocol_start_connection().

◆ threaded_send_terminal_size_with_auto_detect()

asciichat_error_t threaded_send_terminal_size_with_auto_detect ( unsigned short  width,
unsigned short  height 
)

#include <server.c>

Thread-safe terminal size packet transmission with auto-detection.

Sends terminal capabilities packet to the server including terminal size, color capabilities, and rendering preferences. Auto-detects terminal capabilities if not explicitly specified.

Parameters
widthTerminal width in characters
heightTerminal height in characters
Returns
0 on success, negative on error
Parameters
widthTerminal width in characters
heightTerminal height in characters
Returns
0 on success, negative on error

Definition at line 1362 of file src/client/server.c.

1362 {
1363 // Log the dimensions being sent to server (helps debug dimension mismatch issues)
1364 log_debug("Sending terminal size to server: %ux%u (auto_width=%d, auto_height=%d)", width, height,
1366
1367 // Connection and transport availability is checked by threaded_send_packet()
1368
1369 // Build terminal capabilities packet locally
1370 // Detect terminal capabilities automatically
1372
1373 // Set wants_padding based on snapshot mode and TTY status
1374 // Disable padding when:
1375 // - In snapshot mode (one frame and exit)
1376 // - When stdout is not a TTY (piped/redirected output)
1377 // Enable padding for interactive terminal sessions
1378 bool is_snapshot_mode = GET_OPTION(snapshot_mode);
1379 bool is_interactive = terminal_is_interactive();
1380 caps.wants_padding = is_interactive && !is_snapshot_mode;
1381
1382 log_debug("Client capabilities: wants_padding=%d (snapshot=%d, interactive=%d, stdin_tty=%d, stdout_tty=%d)",
1383 caps.wants_padding, is_snapshot_mode, is_interactive, terminal_is_stdin_tty(), terminal_is_stdout_tty());
1384
1385 // Apply user's color mode override
1386 caps = apply_color_mode_override(caps);
1387
1388 // Check if detection was reliable, use fallback only for auto-detection
1389 if (!caps.detection_reliable && GET_OPTION(color_mode) == COLOR_MODE_AUTO) {
1390 log_warn("Terminal capability detection not reliable, using fallback");
1391 SAFE_MEMSET(&caps, sizeof(caps), 0, sizeof(caps));
1393 caps.color_count = 2;
1394 caps.capabilities = 0;
1395 SAFE_STRNCPY(caps.term_type, "unknown", sizeof(caps.term_type));
1396 SAFE_STRNCPY(caps.colorterm, "", sizeof(caps.colorterm));
1397 caps.detection_reliable = 0;
1398 // Preserve wants_padding even in fallback mode
1399 caps.wants_padding = is_interactive && !is_snapshot_mode;
1400 }
1401
1402 // Convert to network packet format with proper byte order
1404 net_packet.capabilities = HOST_TO_NET_U32(caps.capabilities);
1405 net_packet.color_level = HOST_TO_NET_U32(caps.color_level);
1406 net_packet.color_count = HOST_TO_NET_U32(caps.color_count);
1407 net_packet.render_mode = HOST_TO_NET_U32(caps.render_mode);
1408 net_packet.width = HOST_TO_NET_U16(width);
1409 net_packet.height = HOST_TO_NET_U16(height);
1410 net_packet.palette_type = HOST_TO_NET_U32(GET_OPTION(palette_type));
1411 net_packet.utf8_support = HOST_TO_NET_U32(caps.utf8_support ? 1 : 0);
1412
1413 const options_t *opts = options_get();
1414 if (GET_OPTION(palette_type) == PALETTE_CUSTOM && GET_OPTION(palette_custom_set)) {
1415 const char *palette_custom = opts && opts->palette_custom_set ? opts->palette_custom : "";
1416 SAFE_STRNCPY(net_packet.palette_custom, palette_custom, sizeof(net_packet.palette_custom));
1417 net_packet.palette_custom[sizeof(net_packet.palette_custom) - 1] = '\0';
1418 } else {
1419 SAFE_MEMSET(net_packet.palette_custom, sizeof(net_packet.palette_custom), 0, sizeof(net_packet.palette_custom));
1420 }
1421
1422 // Set desired FPS
1423 int fps = GET_OPTION(fps);
1424 if (fps > 0) {
1425 net_packet.desired_fps = (uint8_t)(fps > 144 ? 144 : fps);
1426 } else {
1427 net_packet.desired_fps = caps.desired_fps;
1428 }
1429
1430 if (net_packet.desired_fps == 0) {
1431 net_packet.desired_fps = DEFAULT_MAX_FPS;
1432 }
1433
1434 SAFE_STRNCPY(net_packet.term_type, caps.term_type, sizeof(net_packet.term_type));
1435 net_packet.term_type[sizeof(net_packet.term_type) - 1] = '\0';
1436
1437 SAFE_STRNCPY(net_packet.colorterm, caps.colorterm, sizeof(net_packet.colorterm));
1438 net_packet.colorterm[sizeof(net_packet.colorterm) - 1] = '\0';
1439
1440 net_packet.detection_reliable = caps.detection_reliable;
1441 // Send UTF-8 support flag: true for AUTO (default) and TRUE settings, false for FALSE setting
1442 net_packet.utf8_support = (GET_OPTION(force_utf8) != UTF8_SETTING_FALSE) ? 1 : 0;
1443
1444 // Set wants_padding flag (1=padding enabled, 0=no padding for snapshot/piped modes)
1445 net_packet.wants_padding = caps.wants_padding ? 1 : 0;
1446
1447 // Set codec capabilities (client supports all codecs by default)
1450
1451 // Use threaded_send_packet() which handles encryption
1452 return threaded_send_packet(PACKET_TYPE_CLIENT_CAPABILITIES, &net_packet, sizeof(net_packet));
1453}
#define HOST_TO_NET_U16(val)
Definition endian.h:96
#define DEFAULT_MAX_FPS
Default maximum frame rate (frames per second)
Definition limits.h:29
#define AUDIO_CODEC_CAP_ALL
Default audio codec capabilities (all codecs supported)
Definition codecs.h:77
#define VIDEO_CODEC_CAP_ALL
Default video codec capabilities (all codecs supported)
Definition codecs.h:50
#define COLOR_MODE_AUTO
Backward compatibility aliases for color mode enum values.
const options_t * options_get(void)
Get current options (lock-free read)
Definition rcu.c:496
@ UTF8_SETTING_FALSE
Force UTF-8 OFF: disable UTF-8 support.
@ PACKET_TYPE_CLIENT_CAPABILITIES
Client reports terminal capabilities.
Definition packet.h:381
@ PALETTE_CUSTOM
User-defined via –palette-chars.
Definition palette.h:98
terminal_capabilities_t apply_color_mode_override(terminal_capabilities_t caps)
Apply command-line overrides to detected capabilities.
Definition misc.c:29
bool auto_height
bool auto_width
bool terminal_is_interactive(void)
Check if the session is fully interactive.
bool terminal_is_stdin_tty(void)
Check if stdin is connected to a TTY.
bool terminal_is_stdout_tty(void)
Check if stdout is connected to a TTY.
terminal_capabilities_t detect_terminal_capabilities(void)
Detect terminal capabilities.
Consolidated options structure.
char palette_custom[256]
Custom palette characters.
bool palette_custom_set
True if custom palette was set.
Terminal capabilities packet structure (Packet Type 5)
Definition packet.h:1018
uint32_t codec_capabilities_video
Video codec capabilities bitmask (VIDEO_CODEC_CAP_*)
Definition packet.h:1050
uint32_t capabilities
Terminal capabilities bitmask (TERM_CAP_* flags)
Definition packet.h:1020
uint32_t color_level
Color level enum value (terminal_color_mode_t)
Definition packet.h:1022
uint32_t color_count
Actual color count (16, 256, or 16777216)
Definition packet.h:1024
uint16_t height
Terminal height in characters.
Definition packet.h:1030
uint8_t wants_padding
Whether client wants frame padding (1=padding enabled, 0=no padding for snapshot/piped modes)
Definition packet.h:1048
uint32_t render_mode
Render mode enum value (foreground/background/half-block)
Definition packet.h:1026
uint16_t width
Terminal width in characters.
Definition packet.h:1028
uint8_t desired_fps
Client's desired frame rate (1-144 FPS)
Definition packet.h:1044
uint8_t detection_reliable
Detection reliability flag (1=reliable detection, 0=best guess)
Definition packet.h:1036
uint32_t codec_capabilities_audio
Audio codec capabilities bitmask (AUDIO_CODEC_CAP_*)
Definition packet.h:1052
char palette_custom[64]
Custom palette characters (if palette_type == PALETTE_CUSTOM)
Definition packet.h:1042
char colorterm[32]
$COLORTERM environment variable value (for debugging)
Definition packet.h:1034
uint32_t palette_type
Palette type enum value (palette_type_t)
Definition packet.h:1040
uint32_t utf8_support
UTF-8 support flag (0=no UTF-8, 1=UTF-8 supported)
Definition packet.h:1038
char term_type[32]
$TERM environment variable value (for debugging)
Definition packet.h:1032
Complete terminal capabilities structure.
Definition terminal.h:709
terminal_color_mode_t color_level
Detected color support level (terminal_color_mode_t)
Definition terminal.h:711
uint8_t desired_fps
Client's desired frame rate (1-144 FPS)
Definition terminal.h:733
render_mode_t render_mode
Preferred rendering mode (render_mode_t)
Definition terminal.h:721
bool utf8_support
True if terminal supports UTF-8 encoding.
Definition terminal.h:717
bool wants_padding
Whether client wants frame padding (centering) - false for snapshot/piped modes.
Definition terminal.h:737
uint32_t capabilities
Capability flags bitmask (terminal_capability_flags_t)
Definition terminal.h:713
uint32_t color_count
Maximum number of colors (2, 16, 256, or 16777216)
Definition terminal.h:715
char term_type[64]
$TERM environment variable value (for debugging)
Definition terminal.h:723
char colorterm[64]
$COLORTERM environment variable value (for debugging)
Definition terminal.h:725
bool detection_reliable
True if detection is confident (reliable detection)
Definition terminal.h:719
@ TERM_COLOR_NONE
No color support (monochrome terminal)
Definition terminal.h:582

References apply_color_mode_override(), AUDIO_CODEC_CAP_ALL, auto_height, auto_width, terminal_capabilities_packet_t::capabilities, terminal_capabilities_t::capabilities, terminal_capabilities_packet_t::codec_capabilities_audio, terminal_capabilities_packet_t::codec_capabilities_video, terminal_capabilities_packet_t::color_count, terminal_capabilities_t::color_count, terminal_capabilities_packet_t::color_level, terminal_capabilities_t::color_level, COLOR_MODE_AUTO, terminal_capabilities_packet_t::colorterm, terminal_capabilities_t::colorterm, DEFAULT_MAX_FPS, terminal_capabilities_packet_t::desired_fps, terminal_capabilities_t::desired_fps, detect_terminal_capabilities(), terminal_capabilities_packet_t::detection_reliable, terminal_capabilities_t::detection_reliable, GET_OPTION, terminal_capabilities_packet_t::height, HOST_TO_NET_U16, HOST_TO_NET_U32, log_debug, log_warn, options_get(), PACKET_TYPE_CLIENT_CAPABILITIES, terminal_capabilities_packet_t::palette_custom, PALETTE_CUSTOM, terminal_capabilities_packet_t::palette_type, terminal_capabilities_packet_t::render_mode, terminal_capabilities_t::render_mode, SAFE_MEMSET, SAFE_STRNCPY, TERM_COLOR_NONE, terminal_capabilities_packet_t::term_type, terminal_capabilities_t::term_type, terminal_is_interactive(), terminal_is_stdin_tty(), terminal_is_stdout_tty(), threaded_send_packet(), UTF8_SETTING_FALSE, terminal_capabilities_packet_t::utf8_support, terminal_capabilities_t::utf8_support, VIDEO_CODEC_CAP_ALL, terminal_capabilities_packet_t::wants_padding, terminal_capabilities_t::wants_padding, and terminal_capabilities_packet_t::width.

Referenced by protocol_start_connection(), and server_connection_establish().

Variable Documentation

◆ g_crypto_ctx

crypto_handshake_context_t g_crypto_ctx = {0}

#include <server.c>

Per-connection crypto handshake context.

Global crypto handshake context for this client connection.

Maintains the cryptographic state for the current connection, including key exchange state, encryption keys, and handshake progress.

Note
This is not static because it may be accessed from crypto.c

Definition at line 213 of file src/client/server.c.

213{0};

Referenced by client_crypto_handshake(), client_crypto_init(), crypto_client_cleanup(), crypto_client_decrypt_packet(), crypto_client_encrypt_packet(), crypto_client_get_context(), crypto_client_initiate_rekey(), crypto_client_is_ready(), crypto_client_process_rekey_request(), crypto_client_process_rekey_response(), crypto_client_send_rekey_complete(), crypto_client_send_rekey_response(), crypto_client_should_rekey(), and server_connection_close().