41#include <netinet/tcp.h>
56#define MAX_RECONNECT_DELAY (5 * 1000 * 1000)
63static unsigned int get_reconnect_delay(
unsigned int attempt) {
64 unsigned int delay_us = 100000 + (attempt - 1) * 200000;
71static int close_socket_safe(
socket_t sockfd) {
98 log_error(
"Failed to allocate tcp_client_t");
103 memset(client, 0,
sizeof(*client));
116 log_error(
"Failed to initialize send mutex");
131 log_error(
"Failed to initialize audio queue mutex");
138 log_error(
"Failed to initialize audio queue cond");
173 log_debug(
"TCP client created successfully");
183 if (!client_ptr || !*client_ptr) {
192 log_warn(
"Destroying client while audio capture thread may still be running");
195 log_warn(
"Destroying client while data thread may still be running");
198 log_warn(
"Destroying client while capture thread may still be running");
201 log_warn(
"Destroying client while ping thread may still be running");
207 close_socket_safe(client->
sockfd);
273 log_info(
"Connection lost signaled");
291 close_socket_safe(client->
sockfd);
364 log_debug(
"Failed to send packet type %d: %s", type, asciichat_error_string(result));
412 bool first_connection,
bool has_ever_connected) {
413 (void)first_connection;
414 (void)has_ever_connected;
416 if (!client || !address || port <= 0) {
422 close_socket_safe(client->
sockfd);
427 if (reconnect_attempt > 0) {
428 unsigned int delay_us = get_reconnect_delay(reconnect_attempt);
435 (strcmp(address,
"localhost") == 0 || strcmp(address,
"127.0.0.1") == 0 || strcmp(address,
"::1") == 0);
437 struct addrinfo hints, *res = NULL, *addr_iter;
438 memset(&hints, 0,
sizeof(hints));
439 hints.ai_family = AF_UNSPEC;
440 hints.ai_socktype = SOCK_STREAM;
442 hints.ai_flags = AI_NUMERICSERV;
450 log_debug(
"Localhost detected - trying IPv6 loopback [::1]:%s first...", port_str);
451 hints.ai_family = AF_INET6;
452 hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
454 int ipv6_result = getaddrinfo(
"::1", port_str, &hints, &res);
455 if (ipv6_result == 0 && res != NULL) {
459 log_info(
"Trying IPv6 loopback connection to [::1]:%s...", port_str);
461 log_debug(
"Connection successful using IPv6 loopback");
465 goto connection_success;
467 close_socket_safe(client->
sockfd);
475 log_debug(
"IPv6 failed, trying IPv4 loopback 127.0.0.1:%s...", port_str);
476 hints.ai_family = AF_INET;
478 int ipv4_result = getaddrinfo(
"127.0.0.1", port_str, &hints, &res);
479 if (ipv4_result == 0 && res != NULL) {
482 log_info(
"Trying IPv4 loopback connection to 127.0.0.1:%s...", port_str);
484 log_debug(
"Connection successful using IPv4 loopback");
488 goto connection_success;
490 close_socket_safe(client->
sockfd);
498 log_warn(
"Could not connect to localhost using either IPv6 or IPv4 loopback");
503 log_debug(
"Resolving server address '%s' port %s...", address, port_str);
504 hints.ai_family = AF_UNSPEC;
506 int getaddr_result = getaddrinfo(address, port_str, &hints, &res);
507 if (getaddr_result != 0) {
508 log_error(
"Failed to resolve server address '%s': %s", address, gai_strerror(getaddr_result));
513 for (
int address_family = AF_INET6; address_family >= AF_INET; address_family -= (AF_INET6 - AF_INET)) {
514 for (addr_iter = res; addr_iter != NULL; addr_iter = addr_iter->ai_next) {
515 if (addr_iter->ai_family != address_family) {
519 client->
sockfd =
socket_create(addr_iter->ai_family, addr_iter->ai_socktype, addr_iter->ai_protocol);
524 if (addr_iter->ai_family == AF_INET) {
526 }
else if (addr_iter->ai_family == AF_INET6) {
531 log_debug(
"Connection successful using %s", addr_iter->ai_family == AF_INET ?
"IPv4"
532 : addr_iter->ai_family == AF_INET6 ?
"IPv6"
533 :
"unknown protocol");
540 log_warn(
"Failed to format server IP address");
543 goto connection_success;
546 close_socket_safe(client->
sockfd);
559 log_warn(
"Could not connect to server %s:%d (tried all addresses)", address, port);
564 struct sockaddr_storage local_addr = {0};
565 socklen_t addr_len =
sizeof(local_addr);
566 if (getsockname(client->
sockfd, (
struct sockaddr *)&local_addr, &addr_len) == -1) {
568 close_socket_safe(client->
sockfd);
575 if (((
struct sockaddr *)&local_addr)->sa_family == AF_INET) {
576 local_port =
NET_TO_HOST_U16(((
struct sockaddr_in *)&local_addr)->sin_port);
577 }
else if (((
struct sockaddr *)&local_addr)->sa_family == AF_INET6) {
578 local_port =
NET_TO_HOST_U16(((
struct sockaddr_in6 *)&local_addr)->sin6_port);
600 log_debug(
"Connection established successfully to %s:%d (client_id=%u)", address, port, client->
my_client_id);
619 int frame_duration) {
620 if (!client || !opus_data) {
643 size_t header_size = 16;
644 size_t total_size = header_size + opus_size;
656 memcpy(buf + 4, &fd, 4);
657 memset(buf + 8, 0, 8);
660 memcpy(buf + header_size, opus_data, opus_size);
691 const uint16_t *frame_sizes,
int frame_count) {
692 if (!client || !opus_data || !frame_sizes) {
750 log_warn(
"Terminal capability detection not reliable, using fallback");
773 const char *palette_custom = opts && opts->palette_custom_set ? opts->palette_custom :
"";
825 SAFE_MEMSET(&join_packet,
sizeof(join_packet), 0,
sizeof(join_packet));
831 if (send_result == 0) {
842 "CLIENT_JOIN sent (display=\"%s\", capabilities=0x%x)", join_packet.
display_name, capabilities);
879 if (!client || !samples) {
๐ Cross-platform abstraction layer umbrella header for ascii-chat
โ ๏ธโผ๏ธ Error and/or exit() when things go bad.
๐๏ธ Lock-Free Unified Memory Buffer Pool with Lazy Allocation
const crypto_context_t * crypto_handshake_get_context(const crypto_handshake_context_t *ctx)
Get the crypto context for encryption/decryption.
bool crypto_handshake_is_ready(const crypto_handshake_context_t *ctx)
Check if handshake is complete and encryption is ready.
Common declarations and data structures for cryptographic handshake.
๐ Network byte order conversion helpers
#define HOST_TO_NET_U16(val)
#define HOST_TO_NET_U32(val)
#define NET_TO_HOST_U16(val)
void buffer_pool_free(buffer_pool_t *pool, 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)
#define SAFE_STRNCPY(dst, src, size)
#define SAFE_MEMSET(dest, dest_size, ch, count)
#define SAFE_MALLOC(size, cast)
#define SAFE_SNPRINTF(buffer, buffer_size,...)
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
asciichat_error_t
Error and exit codes - unified status values (0-255)
#define DEFAULT_MAX_FPS
Default maximum frame rate (frames per second)
#define MAX_DISPLAY_NAME_LEN
Maximum display name length in characters.
int g_max_fps
Runtime configurable maximum frame rate (can be overridden via environment or command line)
#define log_warn(...)
Log a WARN message.
#define log_error(...)
Log an ERROR message.
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.
#define log_info(...)
Log an INFO message.
#define log_debug(...)
Log a DEBUG message.
@ REMOTE_LOG_DIRECTION_CLIENT_TO_SERVER
asciichat_error_t send_packet_secure(socket_t sockfd, packet_type_t type, const void *data, size_t len, crypto_context_t *crypto_ctx)
Send a packet with encryption and compression support.
uint32_t capabilities
Terminal capabilities bitmask (TERM_CAP_* flags)
uint32_t color_level
Color level enum value (terminal_color_mode_t)
uint32_t color_count
Actual color count (16, 256, or 16777216)
char display_name[MAX_DISPLAY_NAME_LEN]
User display name (null-terminated, max MAX_DISPLAY_NAME_LEN bytes)
#define CONNECT_TIMEOUT
Connection timeout in seconds (3 seconds)
uint16_t height
Terminal height in characters.
asciichat_error_t send_audio_batch_packet(socket_t sockfd, const float *samples, int num_samples, int batch_count, crypto_context_t *crypto_ctx)
Send a batched audio packet with encryption support.
uint8_t reserved[2]
Reserved bytes for alignment (must be zero)
asciichat_error_t av_send_audio_opus_batch(socket_t sockfd, const uint8_t *opus_data, size_t opus_size, const uint16_t *frame_sizes, int sample_rate, int frame_duration, int frame_count, crypto_context_t *crypto_ctx)
Send Opus-encoded audio batch packet with encryption support.
bool connect_with_timeout(socket_t sockfd, const struct sockaddr *addr, socklen_t addrlen, int timeout_seconds)
Connect with timeout.
uint32_t render_mode
Render mode enum value (foreground/background/half-block)
asciichat_error_t packet_send(socket_t sockfd, packet_type_t type, const void *data, size_t len)
Send a packet with proper header and CRC32.
uint16_t width
Terminal width in characters.
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.
uint8_t desired_fps
Client's desired frame rate (1-144 FPS)
uint32_t client_id
Unique client identifier (1-9, 0 = server)
uint8_t detection_reliable
Detection reliability flag (1=reliable detection, 0=best guess)
char palette_custom[64]
Custom palette characters (if palette_type == PALETTE_CUSTOM)
char colorterm[32]
$COLORTERM environment variable value (for debugging)
uint32_t palette_type
Palette type enum value (palette_type_t)
uint32_t capabilities
Client capabilities bitmask (CLIENT_CAP_VIDEO | CLIENT_CAP_AUDIO | CLIENT_CAP_COLOR | CLIENT_CAP_STRE...
uint32_t utf8_support
UTF-8 support flag (0=no UTF-8, 1=UTF-8 supported)
char term_type[32]
$TERM environment variable value (for debugging)
#define GET_OPTION(field)
Safely get a specific option field (lock-free read)
#define COLOR_MODE_AUTO
Backward compatibility aliases for color mode enum values.
const options_t * options_get(void)
Get current options (lock-free read)
packet_type_t
Network protocol packet type enumeration.
@ PACKET_TYPE_AUDIO_OPUS
Opus-encoded single audio frame.
@ PACKET_TYPE_PONG
Keepalive pong response.
@ PACKET_TYPE_STREAM_START
Client requests to start sending video/audio.
@ PACKET_TYPE_CLIENT_JOIN
Client announces capability to send media.
@ PACKET_TYPE_CLIENT_CAPABILITIES
Client reports terminal capabilities.
@ PACKET_TYPE_PING
Keepalive ping packet.
@ PALETTE_CUSTOM
User-defined via โpalette-chars.
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.
๐ IP Address Parsing and Formatting Utilities
tcp_client_t * tcp_client_create(void)
Create and initialize TCP client.
int tcp_client_send_stream_start(tcp_client_t *client, uint32_t stream_type)
Send stream start packet.
socket_t tcp_client_get_socket(const tcp_client_t *client)
Get current socket descriptor.
bool tcp_client_is_active(const tcp_client_t *client)
Check if connection is currently active.
int tcp_client_send_pong(tcp_client_t *client)
Send pong packet.
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_join(tcp_client_t *client, const char *display_name, uint32_t capabilities)
Send client join packet.
void tcp_client_close(tcp_client_t *client)
Close connection gracefully.
int tcp_client_send_ping(tcp_client_t *client)
Send ping packet.
int tcp_client_send_audio_batch(tcp_client_t *client, const float *samples, int num_samples, int batch_count)
Send audio batch packet.
int tcp_client_send_audio_opus(tcp_client_t *client, const uint8_t *opus_data, size_t opus_size, int sample_rate, int frame_duration)
Send Opus-encoded audio frame.
bool tcp_client_is_lost(const tcp_client_t *client)
Check if connection was lost.
#define MAX_RECONNECT_DELAY
void tcp_client_shutdown(tcp_client_t *client)
Shutdown connection forcefully (for signal handlers)
int tcp_client_send_terminal_capabilities(tcp_client_t *client, unsigned short width, unsigned short height)
Send terminal capabilities packet.
int tcp_client_send_audio_opus_batch(tcp_client_t *client, const uint8_t *opus_data, size_t opus_size, const uint16_t *frame_sizes, int frame_count)
Send Opus audio batch packet.
uint32_t tcp_client_get_id(const tcp_client_t *client)
Get client ID assigned by server.
void tcp_client_cleanup(tcp_client_t *client)
Cleanup connection resources.
void tcp_client_signal_lost(tcp_client_t *client)
Signal that connection was lost (triggers reconnection)
void tcp_client_destroy(tcp_client_t **client_ptr)
Destroy TCP client and free resources.
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.
๐ Logging API with multiple log levels and terminal output control
๐ Core network I/O operations with timeout support
โ๏ธ Command-line options parsing and configuration management for ascii-chat
Packet protocol implementation with encryption and compression support.
Cross-platform process execution utilities.
Cross-platform socket interface for ascii-chat.
Client information packet structure.
Cryptographic context structure.
Consolidated options structure.
TCP client connection and state management.
crypto_handshake_context_t crypto_ctx
bool capture_thread_created
atomic_bool connection_active
atomic_bool should_reconnect
cond_t audio_send_queue_cond
bool should_clear_before_next_frame
atomic_bool ping_thread_exited
bool server_state_initialized
int audio_send_queue_head
atomic_bool audio_capture_thread_exited
mutex_t audio_send_queue_mutex
int audio_send_queue_tail
atomic_bool audio_sender_should_exit
atomic_bool connection_lost
uint32_t last_active_count
audio_context_t audio_ctx
bool audio_sender_thread_created
bool audio_send_queue_initialized
bool audio_capture_thread_created
atomic_bool capture_thread_exited
tcp_client_audio_packet_t audio_send_queue[256]
atomic_bool is_first_frame_of_connection
atomic_bool data_thread_exited
Terminal capabilities packet structure (Packet Type 5)
Complete terminal capabilities structure.
terminal_color_mode_t color_level
Detected color support level (terminal_color_mode_t)
uint8_t desired_fps
Client's desired frame rate (1-144 FPS)
render_mode_t render_mode
Preferred rendering mode (render_mode_t)
bool utf8_support
True if terminal supports UTF-8 encoding.
uint32_t capabilities
Capability flags bitmask (terminal_capability_flags_t)
uint32_t color_count
Maximum number of colors (16, 256, or 16777216)
char term_type[64]
$TERM environment variable value (for debugging)
char colorterm[64]
$COLORTERM environment variable value (for debugging)
bool detection_reliable
True if detection is confident (reliable detection)
Cross-platform system functions interface for ascii-chat.
๐ฅ๏ธ Cross-platform terminal interface for ascii-chat
โฑ๏ธ High-precision timing utilities using sokol_time.h and uthash
Common SIMD utilities and structures.