mpx_session -- run a multiplexed network service on a given port
#include <afblib/mpx_session.h>
typedef struct session { void* handle; stralloc request; stralloc response; } session;
typedef void (*request_handler)(session* s); typedef void (*hangup_handler)(session* s);
void send_response(session* s); void close_session(session* s); void run_mpx_service(in_port_t port, request_handler rhandler, hangup_handler hhandler);
run_mpx_service creates a socket that listens on port, accepts all incoming connections, invokes rhandler for every newline-terminated input line from any of the network connections, and calls hhandler whenever a network connection terminates.
Request handlers get a session reference with following fields:
This field is initially null and may be freely used to maintain a pointer to a custom session object.
This stralloc object contains one input line from the given connection. The terminating line-feed, and a possibly preceding carriage-return are already removed.
This stralloc object is initially empty whenever a request handler gets called. This string object may be filled (without terminating null-bytes, carriage-returns or line-feeds) to create a response packet. If this field remains empty, no response is sent back. Instead, a delayed reponse may be sent any time later on by using send_response.
close_session may be called to initiate the shutdown of a session, i.e. no further input packets will be processed, just the pending response packets will be taken care of.
run_mpx_service runs normally infinitely and returns in error cases only.
Following example implements a network service that allows to increment whole numbers. Usually, every session has its own number that gets incremented. But, by adding the keyword global to an increment, a global counter that is shared over all sessions gets incremented.
#include <afblib/mpx_session.h> #include <afblib/tokenizer.h> #include <stralloc.h> #include <stdlib.h> #include <stdio.h> #include <string.h>
struct status { int count; // per-session counter }; int global_counter = 0; // global counter, shared by all sessions
void handle_request(session* s) { if (s->handle == 0) { // new network connection, create a new status object for it struct status* sp = (struct status*) malloc(sizeof(struct status)); if (sp == 0) return; sp->count = 0; s->handle = (void*) sp; } struct status* sp = (struct status*) s->handle; strlist tokens = {0}; stralloc_0(&s->request); // 0-termination required by tokenizer if (!tokenizer(&s->request, &tokens)) return; int value = 0; if (tokens.len > 0) { int increment; if (sscanf(tokens.list[0], "%d", &increment) == 1) { if (tokens.len == 2 && strcmp(tokens.list[1], "global") == 0) { global_counter += increment; value = global_counter; } else { sp->count += increment; value = sp->count; } } } // return current value of the chosen counter as response stralloc_catint(&s->response, value); }
void handle_close(session* s) { if (s->handle) { free(s->handle); } }
int main() { run_mpx_service(47110, handle_request, handle_close); }
Andreas F. Borchert