diff --git a/examples/server_data_push/Makefile b/examples/server_data_push/Makefile new file mode 100644 index 00000000..8e19d30b --- /dev/null +++ b/examples/server_data_push/Makefile @@ -0,0 +1,12 @@ +# Copyright (c) 2015 Cesanta Software +# All rights reserved + +PROG = server_data_push +CFLAGS = -W -Wall -I../.. -pthread -g -O0 -DMONGOOSE_ENABLE_THREADS $(CFLAGS_EXTRA) +SOURCES = $(PROG).c ../../mongoose.c + +$(PROG): $(SOURCES) + $(CC) -o $(PROG) $(SOURCES) $(CFLAGS) + +clean: + rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib diff --git a/examples/server_data_push/server_data_push.c b/examples/server_data_push/server_data_push.c new file mode 100644 index 00000000..86ace5d6 --- /dev/null +++ b/examples/server_data_push/server_data_push.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2015 Cesanta Software Limited + * + * In this example we have a background data producer that periodically + * produces a piece of data, which is then distributed to all connected + * WebSocket clients. + * + * Data producing thread is outside of the event loop so synchronization + * using a mutex is required. + */ + +#include +#include +#include +#include + +#include + +#include "mongoose.h" + +static pthread_mutex_t s_data_lock = PTHREAD_MUTEX_INITIALIZER; +static int s_data_version = 0; +static char s_data[100]; + +void *data_producer(void *arg) { + (void) arg; + fprintf(stderr, "Data producer running\n"); + srand(time(NULL)); + while (1) { + pthread_mutex_lock(&s_data_lock); + snprintf(s_data, sizeof(s_data), "The lucky number is %d.", rand() % 100); + s_data_version++; + pthread_mutex_unlock(&s_data_lock); + sleep(1 + rand() % 10); + } +} + +struct conn_state { + int data_version; + /* More state goes here. */ +}; + +void maybe_send_data(struct mg_connection *conn) { + struct conn_state *cs = (struct conn_state *) conn->connection_param; + if (cs == NULL) return; /* Not connected yet. */ + pthread_mutex_lock(&s_data_lock); + if (cs->data_version != s_data_version) { + mg_websocket_printf(conn, WEBSOCKET_OPCODE_TEXT, "%s\n", s_data); + cs->data_version = s_data_version; + } + pthread_mutex_unlock(&s_data_lock); +} + +static int ev_handler(struct mg_connection *conn, enum mg_event ev) { + switch (ev) { + case MG_WS_CONNECT: + fprintf(stderr, "%s:%u joined\n", conn->remote_ip, conn->remote_port); + conn->connection_param = calloc(1, sizeof(struct conn_state)); + mg_websocket_printf(conn, WEBSOCKET_OPCODE_TEXT, "Hi %p!\n", conn); + maybe_send_data(conn); + return MG_FALSE; /* Keep the connection open. */ + case MG_POLL: + maybe_send_data(conn); + return MG_FALSE; /* Keep the connection open. */ + case MG_REQUEST: + /* FIXME: This is a bug in Mongoose - ping requests are generated internally + * but responses fall through to the user who may not expect them. */ + if (conn->is_websocket && (conn->wsbits & 0xf) == WEBSOCKET_OPCODE_PONG) { + return MG_TRUE; + } + return MG_FALSE; + case MG_CLOSE: + fprintf(stderr, "%s:%u went away\n", conn->remote_ip, conn->remote_port); + free(conn->connection_param); + conn->connection_param = NULL; + return MG_TRUE; + case MG_AUTH: + return MG_TRUE; /* Authenticated. */ + default: + return MG_FALSE; + } +} + +int main(void) { + const char *listen_port = "8080"; + struct mg_server *server; + const char *err; + + server = mg_create_server(NULL, ev_handler); + err = mg_set_option(server, "listening_port", listen_port); + if (err != NULL) { + fprintf(stderr, "Error setting up listener on %s: %s\n", listen_port, err); + } + + mg_start_thread(data_producer, NULL); + + printf("Listening on %s\n", listen_port); + while (1) { + mg_poll_server(server, 100); + } + + return 0; +}