ascii-chat 0.6.0
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
server.h File Reference

🌐 Discovery server TCP connection manager More...

Go to the source code of this file.

Data Structures

struct  acds_client_data_t
 Per-client connection data. More...
 
struct  acds_server_t
 Discovery server state. More...
 

Functions

asciichat_error_t acds_server_init (acds_server_t *server, const acds_config_t *config)
 Initialize discovery server.
 
asciichat_error_t acds_server_run (acds_server_t *server)
 Run discovery server main loop.
 
void acds_server_shutdown (acds_server_t *server)
 Shutdown discovery server.
 
void * acds_client_handler (void *arg)
 Per-client connection handler (thread entry point)
 

Detailed Description

🌐 Discovery server TCP connection manager

Implements the core TCP server for the discovery service. Handles client connections, crypto handshakes, and dispatches packets to appropriate handlers (session management, WebRTC signaling).

Architecture

  • Main thread accepts connections on TCP socket
  • Per-client handler threads process ACIP packets
  • Reuses lib/network/ for packet I/O
  • Session registry protected by rwlock
  • SQLite for persistence

Lifecycle

  1. acds_server_init() - Create server, load identity, open database
  2. acds_server_run() - Accept connections, spawn handlers
  3. acds_server_shutdown() - Close sockets, stop threads, cleanup

Definition in file src/acds/server.h.

Function Documentation

◆ acds_client_handler()

void * acds_client_handler ( void *  arg)

Per-client connection handler (thread entry point)

Processes ACIP packets from a connected client. Handles crypto handshake, then dispatches packets to session/signaling handlers.

Parameters
argPointer to client connection context
Returns
NULL (thread exit value)

Definition at line 639 of file src/acds/server.c.

639 {
641 if (!ctx) {
642 log_error("Client handler: NULL context");
643 return NULL;
644 }
645
646 acds_server_t *server = (acds_server_t *)ctx->user_data;
647 socket_t client_socket = ctx->client_socket;
648
649 // Get client IP for logging
650 char client_ip[INET6_ADDRSTRLEN] = {0};
651 tcp_client_context_get_ip(ctx, client_ip, sizeof(client_ip));
652
653 /* Register this client handler thread with RCU library before any RCU operations.
654 This ensures the thread is tracked in the RCU synchronization scheme for
655 lock-free hash table access. */
656 rcu_register_thread();
657 log_info("Client handler started for %s (RCU registered)", client_ip);
658
659 // Register client in TCP server registry with allocated client data
661 if (!client_data) {
662 tcp_server_reject_client(client_socket, "Failed to allocate client data");
663 SAFE_FREE(ctx);
664 rcu_unregister_thread();
665 return NULL;
666 }
667 memset(client_data, 0, sizeof(*client_data));
668 client_data->joined_session = false;
669
670 if (tcp_server_add_client(&server->tcp_server, client_socket, client_data) != ASCIICHAT_OK) {
671 SAFE_FREE(client_data);
672 tcp_server_reject_client(client_socket, "Failed to register client in registry");
673 SAFE_FREE(ctx);
674 rcu_unregister_thread();
675 return NULL;
676 }
677
678 log_debug("Client %s registered (socket=%d, total=%zu)", client_ip, client_socket,
679 tcp_server_get_client_count(&server->tcp_server));
680
681 // TODO: Crypto handshake (when crypto support is added)
682 // For now, accept plain connections
683
684 // Main packet processing loop
685 while (atomic_load(&server->tcp_server.running)) {
686 packet_type_t packet_type;
687 void *payload = NULL;
688 size_t payload_size = 0;
689
690 // Receive packet (blocking with system timeout)
691 int result = receive_packet(client_socket, &packet_type, &payload, &payload_size);
692 if (result < 0) {
693 // Connection error - client disconnected
694 log_info("Client %s disconnected", client_ip);
695 if (payload) {
696 buffer_pool_free(NULL, payload, payload_size);
697 }
698 break;
699 }
700
701 log_debug("Received packet type 0x%02X from %s, length=%zu", packet_type, client_ip, payload_size);
702
703 // O(1) ACIP array-based dispatch
704 // Set server context for callbacks
705 acip_acds_callbacks_t callbacks = g_acds_callbacks;
706 callbacks.app_ctx = server;
707
708 asciichat_error_t dispatch_result =
709 acip_handle_acds_packet(NULL, packet_type, payload, payload_size, client_socket, client_ip, &callbacks);
710
711 if (dispatch_result != ASCIICHAT_OK) {
712 log_warn("ACIP handler failed for packet type 0x%02X from %s: %s", packet_type, client_ip,
713 asciichat_error_string(dispatch_result));
714 }
715
716 // Free payload (allocated by receive_packet via buffer_pool_alloc)
717 if (payload) {
718 buffer_pool_free(NULL, payload, payload_size);
719 }
720 }
721
722 // Cleanup
723 tcp_server_remove_client(&server->tcp_server, client_socket);
724 log_debug("Client %s unregistered (total=%zu)", client_ip, tcp_server_get_client_count(&server->tcp_server));
725
726 socket_close(client_socket);
727 SAFE_FREE(ctx);
728
729 rcu_unregister_thread();
730 log_info("Client handler finished for %s (RCU unregistered)", client_ip);
731 return NULL;
732}
asciichat_error_t acip_handle_acds_packet(acip_transport_t *transport, packet_type_t type, const void *payload, size_t payload_len, int client_socket, const char *client_ip, const acip_acds_callbacks_t *callbacks)
Handle incoming ACDS packet with O(1) dispatch.
void buffer_pool_free(buffer_pool_t *pool, void *data, size_t size)
Free a buffer back to the pool (lock-free)
#define SAFE_FREE(ptr)
Definition common.h:320
#define SAFE_MALLOC(size, cast)
Definition common.h:208
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:46
@ ASCIICHAT_OK
Definition error_codes.h:48
#define log_warn(...)
Log a WARN message.
#define log_error(...)
Log an ERROR message.
#define log_info(...)
Log an INFO message.
#define log_debug(...)
Log a DEBUG message.
int receive_packet(socket_t sockfd, packet_type_t *type, void **data, size_t *len)
Receive a basic packet without encryption.
Definition packet.c:767
packet_type_t
Network protocol packet type enumeration.
Definition packet.h:281
int socket_t
Socket handle type (POSIX: int)
Definition socket.h:50
int socket_close(socket_t sock)
Close a socket.
void tcp_server_reject_client(socket_t socket, const char *reason)
Reject client connection with reason.
asciichat_error_t tcp_server_remove_client(tcp_server_t *server, socket_t socket)
Remove client from registry.
const char * tcp_client_context_get_ip(const tcp_client_context_t *ctx, char *buf, size_t len)
Get formatted IP address from client context.
size_t tcp_server_get_client_count(tcp_server_t *server)
Get client count.
asciichat_error_t tcp_server_add_client(tcp_server_t *server, socket_t socket, void *client_data)
Add client to registry.
Per-client connection data.
bool joined_session
Whether client has successfully joined a session.
Discovery server state.
ACDS packet handler callbacks.
void * app_ctx
Application context (passed to all callbacks)
Per-client connection context.
socket_t client_socket
Client connection socket.
void * user_data
User-provided data from config.

References acip_handle_acds_packet(), acip_acds_callbacks_t::app_ctx, ASCIICHAT_OK, buffer_pool_free(), tcp_client_context_t::client_socket, acds_client_data_t::joined_session, log_debug, log_error, log_info, log_warn, receive_packet(), tcp_server::running, SAFE_FREE, SAFE_MALLOC, socket_close(), tcp_client_context_get_ip(), acds_server_t::tcp_server, tcp_server_add_client(), tcp_server_get_client_count(), tcp_server_reject_client(), tcp_server_remove_client(), and tcp_client_context_t::user_data.

Referenced by acds_server_init().

◆ acds_server_init()

asciichat_error_t acds_server_init ( acds_server_t server,
const acds_config_t config 
)

Initialize discovery server.

Loads or generates identity keys, opens database, creates session registry, and binds TCP socket.

Parameters
serverServer structure to initialize
configConfiguration from command-line parsing
Returns
ASCIICHAT_OK on success, error code otherwise

Definition at line 86 of file src/acds/server.c.

86 {
87 if (!server || !config) {
88 return SET_ERRNO(ERROR_INVALID_PARAM, "server or config is NULL");
89 }
90
91 memset(server, 0, sizeof(*server));
92 memcpy(&server->config, config, sizeof(acds_config_t));
93
94 // Initialize session registry
96 if (!server->sessions) {
97 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate session registry");
98 }
99
101 if (result != ASCIICHAT_OK) {
102 SAFE_FREE(server->sessions);
103 return result;
104 }
105
106 // Open database
107 result = database_init(config->database_path, &server->db);
108 if (result != ASCIICHAT_OK) {
110 SAFE_FREE(server->sessions);
111 return result;
112 }
113
114 // Load sessions from database
115 result = database_load_sessions(server->db, server->sessions);
116 if (result != ASCIICHAT_OK) {
117 log_warn("Failed to load sessions from database (continuing anyway)");
118 }
119
120 // Initialize rate limiter with SQLite backend
121 server->rate_limiter = rate_limiter_create_sqlite(NULL); // NULL = externally managed DB
122 if (!server->rate_limiter) {
123 database_close(server->db);
125 SAFE_FREE(server->sessions);
126 return SET_ERRNO(ERROR_MEMORY, "Failed to create rate limiter");
127 }
128
129 // Set the database handle for the rate limiter
131
132 // Configure TCP server
133 tcp_server_config_t tcp_config = {
134 .port = config->port,
135 .ipv4_address = (config->address[0] != '\0') ? config->address : NULL,
136 .ipv6_address = (config->address6[0] != '\0') ? config->address6 : NULL,
137 .bind_ipv4 = (config->address[0] != '\0') || (config->address[0] == '\0' && config->address6[0] == '\0'),
138 .bind_ipv6 = (config->address6[0] != '\0') || (config->address[0] == '\0' && config->address6[0] == '\0'),
139 .accept_timeout_sec = 1,
140 .client_handler = acds_client_handler,
141 .user_data = server,
142 };
143
144 // Initialize TCP server
145 result = tcp_server_init(&server->tcp_server, &tcp_config);
146 if (result != ASCIICHAT_OK) {
148 database_close(server->db);
150 SAFE_FREE(server->sessions);
151 return result;
152 }
153
154 // Initialize background worker thread pool
155 atomic_store(&server->shutdown, false);
156 server->worker_pool = thread_pool_create("acds_workers");
157 if (!server->worker_pool) {
158 log_warn("Failed to create worker thread pool");
161 database_close(server->db);
163 SAFE_FREE(server->sessions);
164 return SET_ERRNO(ERROR_MEMORY, "Failed to create worker thread pool");
165 }
166
167 // Spawn rate limit cleanup thread in worker pool
168 if (thread_pool_spawn(server->worker_pool, cleanup_thread_func, server, 0, "rate_limit_cleanup") != ASCIICHAT_OK) {
169 log_warn("Failed to spawn rate limit cleanup thread (continuing without cleanup)");
170 }
171
172 log_info("Discovery server initialized successfully");
173 return ASCIICHAT_OK;
174}
asciichat_error_t database_load_sessions(sqlite3 *db, session_registry_t *registry)
Load active sessions from database into registry.
Definition database.c:106
void database_close(sqlite3 *db)
Close database.
Definition database.c:339
asciichat_error_t database_init(const char *db_path, sqlite3 **db)
Initialize database and create schema.
Definition database.c:58
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
@ ERROR_MEMORY
Definition error_codes.h:53
@ ERROR_INVALID_PARAM
asciichat_error_t tcp_server_init(tcp_server_t *server, const tcp_server_config_t *config)
Initialize TCP server.
void tcp_server_shutdown(tcp_server_t *server)
Shutdown TCP server.
void rate_limiter_destroy(rate_limiter_t *limiter)
Destroy rate limiter and free resources.
Definition rate_limit.c:115
void rate_limiter_set_sqlite_db(rate_limiter_t *limiter, void *db)
Set SQLite database handle for rate limiter.
Definition rate_limit.c:106
rate_limiter_t * rate_limiter_create_sqlite(const char *db_path)
Create SQLite-backed rate limiter.
Definition rate_limit.c:89
void session_registry_destroy(session_registry_t *registry)
Destroy session registry.
Definition session.c:190
asciichat_error_t session_registry_init(session_registry_t *registry)
Initialize session registry.
Definition session.c:169
void * acds_client_handler(void *arg)
Per-client connection handler (thread entry point)
Discovery server configuration.
Definition acds/main.h:71
char address[256]
IPv4 bind address (empty = all interfaces)
Definition acds/main.h:73
char database_path[512]
SQLite database path.
Definition acds/main.h:75
int port
TCP listen port (default 27225)
Definition acds/main.h:72
acds_config_t config
Runtime configuration.
atomic_bool shutdown
Shutdown flag for worker threads.
tcp_server_t tcp_server
TCP server abstraction.
sqlite3 * db
SQLite database handle.
session_registry_t * sessions
In-memory session registry.
struct rate_limiter_s * rate_limiter
SQLite-backed rate limiter.
thread_pool_t * worker_pool
Thread pool for background workers.
Session registry (lock-free RCU)
Definition session.h:92
TCP server configuration.
thread_pool_t * thread_pool_create(const char *pool_name)
Create a new thread pool.
Definition thread_pool.c:12
asciichat_error_t thread_pool_spawn(thread_pool_t *pool, void *(*thread_func)(void *), void *thread_arg, int stop_id, const char *thread_name)
Spawn a worker thread in the pool.
Definition thread_pool.c:65

References acds_client_handler(), acds_config_t::address, acds_config_t::address6, ASCIICHAT_OK, acds_server_t::config, database_close(), database_init(), database_load_sessions(), acds_config_t::database_path, acds_server_t::db, ERROR_INVALID_PARAM, ERROR_MEMORY, log_info, log_warn, tcp_server_config_t::port, acds_config_t::port, acds_server_t::rate_limiter, rate_limiter_create_sqlite(), rate_limiter_destroy(), rate_limiter_set_sqlite_db(), SAFE_FREE, SAFE_MALLOC, session_registry_destroy(), session_registry_init(), acds_server_t::sessions, SET_ERRNO, acds_server_t::shutdown, acds_server_t::tcp_server, tcp_server_init(), tcp_server_shutdown(), thread_pool_create(), thread_pool_spawn(), and acds_server_t::worker_pool.

Referenced by main().

◆ acds_server_run()

asciichat_error_t acds_server_run ( acds_server_t server)

Run discovery server main loop.

Accepts client connections and spawns handler threads. Blocks until shutdown signal received.

Parameters
serverInitialized server structure
Returns
ASCIICHAT_OK on success, error code otherwise

Definition at line 176 of file src/acds/server.c.

176 {
177 if (!server) {
178 return SET_ERRNO(ERROR_INVALID_PARAM, "server is NULL");
179 }
180
181 log_info("Discovery server accepting connections on port %d", server->config.port);
182
183 // Delegate to TCP server abstraction
184 return tcp_server_run(&server->tcp_server);
185}
asciichat_error_t tcp_server_run(tcp_server_t *server)
Run TCP server accept loop.

References acds_server_t::config, ERROR_INVALID_PARAM, log_info, acds_config_t::port, SET_ERRNO, acds_server_t::tcp_server, and tcp_server_run().

Referenced by main().

◆ acds_server_shutdown()

void acds_server_shutdown ( acds_server_t server)

Shutdown discovery server.

Closes listen socket, stops accepting connections, waits for handler threads to exit, closes database, and frees resources.

Parameters
serverServer structure to clean up

Definition at line 187 of file src/acds/server.c.

187 {
188 if (!server) {
189 return;
190 }
191
192 // Signal shutdown to worker threads
193 atomic_store(&server->shutdown, true);
194
195 // Shutdown TCP server (closes listen sockets, stops accept loop)
197
198 // Wait for all client handler threads to exit
199 // We need to stop all active client connections gracefully
200 size_t remaining_clients;
201 int shutdown_attempts = 0;
202 const int max_shutdown_attempts = 100; // 10 seconds (100 * 100ms)
203
204 while ((remaining_clients = tcp_server_get_client_count(&server->tcp_server)) > 0 &&
205 shutdown_attempts < max_shutdown_attempts) {
206 log_debug("Waiting for %zu client handler threads to exit (attempt %d/%d)", remaining_clients,
207 shutdown_attempts + 1, max_shutdown_attempts);
209 shutdown_attempts++;
210 }
211
212 if (remaining_clients > 0) {
213 log_warn("Server shutdown: %zu client handler threads still running after 10 seconds", remaining_clients);
214 } else if (shutdown_attempts > 0) {
215 log_debug("All client handler threads exited gracefully");
216 }
217
218 // Stop and destroy worker thread pool (cleanup thread, etc.)
219 if (server->worker_pool) {
221 server->worker_pool = NULL;
222 log_debug("Worker thread pool stopped");
223 }
224
225 // Destroy rate limiter
226 if (server->rate_limiter) {
228 server->rate_limiter = NULL;
229 }
230
231 // Close database
232 if (server->db) {
233 database_close(server->db);
234 server->db = NULL;
235 }
236
237 // Destroy session registry
238 if (server->sessions) {
240 SAFE_FREE(server->sessions);
241 }
242
243 log_info("Server shutdown complete");
244}
void platform_sleep_ms(unsigned int ms)
Sleep for a specified number of milliseconds.
void thread_pool_destroy(thread_pool_t *pool)
Destroy a thread pool.
Definition thread_pool.c:43

References database_close(), acds_server_t::db, log_debug, log_info, log_warn, platform_sleep_ms(), acds_server_t::rate_limiter, rate_limiter_destroy(), SAFE_FREE, session_registry_destroy(), acds_server_t::sessions, acds_server_t::shutdown, acds_server_t::tcp_server, tcp_server_get_client_count(), tcp_server_shutdown(), thread_pool_destroy(), and acds_server_t::worker_pool.

Referenced by main().