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

🖥️ Server main entry point: multi-client connection manager with per-client rendering threads (60fps video + 172fps audio) More...

Go to the source code of this file.

Functions

int server_main (void)
 Server mode entry point for unified binary.
 

Variables

mixer_t *volatile g_audio_mixer = NULL
 Global audio mixer instance for multi-client audio processing.
 
h265_server_context_t *volatile g_h265_server = NULL
 Global H.265 codec server context.
 
static_cond_t g_shutdown_cond = STATIC_COND_INIT
 Global shutdown condition variable for waking blocked threads.
 
rate_limiter_t * g_rate_limiter = NULL
 Global rate limiter for connection attempts and packet processing.
 
bool g_server_encryption_enabled = false
 Global flag indicating if server encryption is enabled.
 
private_key_t g_server_private_key = {0}
 Global server private key (first identity key, for backward compatibility)
 
private_key_t g_server_identity_keys [MAX_IDENTITY_KEYS] = {0}
 Global server identity keys array (multi-key support)
 
size_t g_num_server_identity_keys = 0
 Number of loaded server identity keys.
 
public_key_t g_client_whitelist [MAX_CLIENTS] = {0}
 Global client public key whitelist.
 
size_t g_num_whitelisted_clients = 0
 Number of whitelisted clients.
 

Detailed Description

🖥️ Server main entry point: multi-client connection manager with per-client rendering threads (60fps video + 172fps audio)

MODULAR COMPONENTS:

  • main.c (this file): Server initialization, signal handling, connection management
  • client.c: Per-client lifecycle, threading, and state management
  • protocol.c: Network packet processing and protocol implementation
  • stream.c: Video mixing, ASCII frame generation, and caching
  • render.c: Per-client rendering threads with rate limiting
  • stats.c: Performance monitoring and resource tracking

CONCURRENCY MODEL:

The server creates multiple thread types per client:

  1. Receive thread: Handles incoming packets from client (protocol.c functions)
  2. Send thread: Manages outgoing packet delivery (client.c)
  3. Video render thread: Generates ASCII frames at 60fps (render.c)
  4. Audio render thread: Mixes audio streams at 172fps (render.c)
  5. Stats logger thread: Periodic performance reporting (stats.c)

Thread Safety:

  • Lock ordering: Always acquire g_client_manager_rwlock BEFORE per-client mutexes
  • Snapshot pattern: Copy client state under mutex, then process without locks
  • Signal-safe shutdown: SIGINT handler only sets flags and closes sockets
  • Deterministic cleanup: Main thread waits for all worker threads before exit

WHY THE REFACTORING:

The original server.c became unmaintainable at 2408+ lines, making it:

  • Too large for LLM context windows (limited AI-assisted development)
  • Difficult for humans to navigate and understand
  • Slow to compile and modify
  • Hard to isolate bugs and add new features

The modular design enables:

  • Faster development cycles (smaller compilation units)
  • Better IDE support (jump-to-definition, IntelliSense)
  • Easier testing and debugging (isolated components)
  • Future extensibility (new protocols, renderers, optimizations)
Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
September 2025
Version
2.0 (Post-Modularization)

Definition in file server/main.c.

Function Documentation

◆ server_main()

int server_main ( void  )

Server mode entry point for unified binary.

This function implements the complete server lifecycle including:

  • Server-specific initialization (crypto, shutdown callback)
  • Network socket setup and binding
  • Main connection accept loop
  • Client lifecycle management
  • Graceful shutdown and cleanup

Options are already parsed by the main dispatcher before this function is called, so they are available via global opt_* variables.

Returns
0 on success, non-zero error code on failure
Example
# Invoked by dispatcher after options are parsed:
ascii-chat server --port 8080
# Options parsed in main.c, then server_main() called

Definition at line 2204 of file server/main.c.

2204 {
2205 // Common initialization (options, logging, lock debugging) now happens in main.c before dispatch
2206
2207 // Register atomic for debug tracking
2209
2210 // Register shutdown check callback for library code
2211 shutdown_register_callback(check_shutdown);
2212
2213 // Initialize crypto after logging is ready
2214 log_debug("Initializing crypto...");
2215 if (init_server_crypto() != 0) {
2216 LOG_ERRNO_IF_SET("Crypto initialization failed");
2217 FATAL(ERROR_CRYPTO, "Crypto initialization failed");
2218 }
2219 log_debug("Crypto initialized successfully");
2220
2221 log_info("ascii-chat server starting...");
2222
2223 int port = GET_OPTION(port);
2224 if (port < 1 || port > 65535) {
2225 log_error("Invalid port configuration: %d", port);
2226 FATAL(ERROR_CONFIG, "Invalid port configuration: %d", port);
2227 }
2228
2231
2232#ifndef NDEBUG
2233 if (stats_init() != 0) {
2234 FATAL(ERROR_THREAD, "Statistics system initialization failed");
2235 }
2236#endif
2237
2238 // Create background worker thread pool for server operations
2239 g_server_worker_pool = thread_pool_create("server_workers");
2240 if (!g_server_worker_pool) {
2241 LOG_ERRNO_IF_SET("Failed to create server worker thread pool");
2242 FATAL(ERROR_MEMORY, "Failed to create server worker thread pool");
2243 }
2244
2245 // Spawn statistics logging thread in worker pool
2246 if (thread_pool_spawn(g_server_worker_pool, stats_logger_thread, NULL, 0, "stats_logger") != ASCIICHAT_OK) {
2247 LOG_ERRNO_IF_SET("Statistics logger thread creation failed");
2248 } else {
2249 log_debug("Statistics logger thread started");
2250 }
2251
2252 // Initialize static server context (populated further in server_init_fn)
2253 // H.265 context is initialized in server_init_fn, not here
2254 g_server_ctx = (server_context_t){
2255 .tcp_server = NULL, // Set in server_init_fn after TCP server is ready
2256 .rate_limiter = g_rate_limiter,
2257 .client_manager = &g_client_manager,
2258 .client_manager_rwlock = &g_client_manager_rwlock,
2259 .server_should_exit = &g_should_exit,
2260 .audio_mixer = g_audio_mixer,
2261 .stats = &g_stats,
2262 .stats_mutex = &g_stats_mutex,
2263 .encryption_enabled = g_server_encryption_enabled,
2264 .server_private_key = &g_server_private_key,
2265 .client_whitelist = g_client_whitelist,
2266 .num_whitelisted_clients = g_num_whitelisted_clients,
2267 .session_host = NULL,
2268 .h265_server = NULL, // Initialized in server_init_fn
2269 };
2270
2272 .init_fn = server_init_fn,
2273 .init_user_data = NULL,
2274 .interrupt_fn = server_handle_sigint,
2275 .cleanup_fn = server_cleanup_fn,
2276 .cleanup_user_data = NULL,
2277 .status_fn = server_status_fn,
2278 .status_user_data = NULL,
2279 .tcp_handler = ascii_chat_client_handler,
2280 .tcp_user_data = &g_server_ctx,
2281 .websocket =
2282 {
2283 .enabled = true,
2284 .handler = websocket_client_handler,
2285 .user_data = &g_server_ctx,
2286 },
2287 .mdns =
2288 {
2289 .enabled = !GET_OPTION(no_mdns_advertise),
2290 .service_name = NULL, // Deferred: advertised after ACDS or mDNS session string is ready
2291 .service_type = "_ascii-chat._tcp",
2292 },
2293 .upnp =
2294 {
2295 .enabled = GET_OPTION(enable_upnp),
2296 .description = "ascii-chat Server",
2297 },
2298 // Raise FD limit for high-client-count scenarios (matches discovery-service policy)
2299 // Server accepts MAX_CLIENTS connections plus WebSocket, TCP, UPnP, mDNS, etc.
2300 // Each client may use multiple FDs (socket, media buffers, file handles)
2301 // Raising to 65536 ensures we don't hit system limits under load
2302 .raise_fd_limit = true,
2303 .fd_limit_target = 65536,
2304 };
2305
2307 return (result == ASCIICHAT_OK) ? 0 : 1;
2308}
#define LOG_ERRNO_IF_SET(message)
Check if any error occurred and log it if so.
#define ATOMIC_REGISTER_AUTO(name, parent_ptr)
Register an atomic variable for debug tracking with automatic name generation.
Definition atomic.h:472
asciichat_error_t thread_pool_spawn(void *pool, void *(*thread_func)(void *), void *thread_arg, int stop_id, const char *name)
Spawn a worker thread in the pool.
atomic_t g_should_exit
Global application exit flag (shared across all modes)
Definition globals.c:34
#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_MEMORY
Definition error_codes.h:56
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_CONFIG
Definition error_codes.h:57
@ ERROR_CRYPTO
Definition error_codes.h:96
@ ERROR_THREAD
#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 log_debug(...)
Log a DEBUG message.
Definition log/log.h:548
#define GET_OPTION(field)
Safely get a specific option field (lock-free read)
const float weight_blue
Blue weight for luminance calculation.
const float weight_red
Red weight for luminance calculation.
const float weight_green
Green weight for luminance calculation.
size_t g_num_whitelisted_clients
Number of whitelisted clients.
bool g_server_encryption_enabled
Global flag indicating if server encryption is enabled.
public_key_t g_client_whitelist[MAX_CLIENTS]
Global client public key whitelist.
private_key_t g_server_private_key
Global server private key (first identity key, for backward compatibility)
void shutdown_register_callback(shutdown_check_fn callback)
Register application's shutdown check function.
Definition common.c:69
void precalc_rgb_palettes(const float red, const float green, const float blue)
Precalculate RGB palettes with color adjustment.
void ascii_simd_init(void)
Initialize SIMD subsystem.
rate_limiter_t * g_rate_limiter
Global rate limiter for connection attempts and packet processing.
mixer_t *volatile g_audio_mixer
Global audio mixer instance for multi-client audio processing.
asciichat_error_t session_server_like_run(const session_server_like_config_t *config)
rwlock_t g_client_manager_rwlock
Reader-writer lock protecting the global client manager.
client_manager_t g_client_manager
Global client manager singleton - central coordination point.
server_stats_t g_stats
Global server statistics structure.
Definition stats.c:163
void * stats_logger_thread(void *arg)
Main statistics collection and reporting thread function.
Definition stats.c:327
int stats_init(void)
Initialize the stats mutex.
Definition stats.c:188
mutex_t g_stats_mutex
Mutex protecting global server statistics.
Definition stats.c:172
Server context - encapsulates all server state.
tcp_server_t * tcp_server
TCP server managing connections.
session_server_like_init_fn init_fn
thread_pool_t * thread_pool_create(int num_threads)

References ascii_simd_init(), ASCIICHAT_OK, ATOMIC_REGISTER_AUTO, ERROR_CONFIG, ERROR_CRYPTO, ERROR_MEMORY, ERROR_THREAD, FATAL, g_audio_mixer, g_client_manager, g_client_manager_rwlock, g_client_whitelist, g_num_whitelisted_clients, g_rate_limiter, g_server_encryption_enabled, g_server_private_key, g_should_exit, g_stats, g_stats_mutex, GET_OPTION, session_server_like_config::init_fn, log_debug, LOG_ERRNO_IF_SET, log_error, log_info, precalc_rgb_palettes(), session_server_like_run(), shutdown_register_callback(), stats_init(), stats_logger_thread(), server_context_t::tcp_server, thread_pool_create(), thread_pool_spawn(), weight_blue, weight_green, and weight_red.

Variable Documentation

◆ g_audio_mixer

mixer_t* volatile g_audio_mixer = NULL

Global audio mixer instance for multi-client audio processing.

Global audio mixer.

The mixer combines audio streams from multiple clients, excluding each client's own audio from their outbound stream (preventing echo). Created once during server initialization and shared by all audio render threads.

THREAD SAFETY: The mixer itself is thread-safe and can be used concurrently by multiple render.c audio threads without external synchronization. During shutdown, set to NULL before destroying to prevent use-after-free.

Definition at line 175 of file server/main.c.

Referenced by add_client(), add_webrtc_client(), client_audio_render_thread(), remove_client(), and server_main().

◆ g_h265_server

h265_server_context_t* volatile g_h265_server = NULL

Global H.265 codec server context.

Shared H.265 decoding context used by all packet handlers to decode client H.265 frames. Each client gets its own decoder instance.

THREAD SAFETY: The h265_server context is thread-safe and can be used concurrently by multiple client handlers. Decoders are per-client. During shutdown, set to NULL before destroying to prevent use-after-free.

Definition at line 187 of file server/main.c.

Referenced by handle_image_frame_h265_packet().

◆ g_rate_limiter

rate_limiter_t* g_rate_limiter = NULL

Global rate limiter for connection attempts and packet processing.

Global connection rate limiter.

In-memory rate limiter to prevent connection flooding and DoS attacks. Tracks connection attempts and packet rates per IP address with configurable limits.

Default limits (from rate_limit.c):

  • RATE_EVENT_CONNECTION: 50 connections per 60 seconds
  • RATE_EVENT_IMAGE_FRAME: 144 FPS (8640 frames/min)
  • RATE_EVENT_AUDIO: 172 FPS (10320 packets/min)
  • RATE_EVENT_PING: 2 Hz (120 pings/min)
  • RATE_EVENT_CLIENT_JOIN: 10 joins per 60 seconds
  • RATE_EVENT_CONTROL: 100 control packets per 60 seconds

THREAD SAFETY: The rate limiter is thread-safe and can be used concurrently from the main accept loop and packet handlers without external synchronization.

See also
main.h for extern declaration

Definition at line 217 of file server/main.c.

Referenced by process_decrypted_packet(), and server_main().

◆ g_shutdown_cond

static_cond_t g_shutdown_cond = STATIC_COND_INIT

Global shutdown condition variable for waking blocked threads.

Used to wake up threads that might be blocked on condition variables (like packet queues) during shutdown. This ensures responsive shutdown even when threads are waiting on blocking operations.

Definition at line 196 of file server/main.c.