| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409 | #include <time.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include "civetweb.h"/* Get an OS independent definition for sleep() */#ifdef _WIN32#include <windows.h>#define sleep(x) Sleep((x)*1000)#else#include <unistd.h>#endif/* User defined client data structure */struct tclient_data {	time_t started;	time_t closed;	struct tmsg_list_elem *msgs;};struct tmsg_list_elem {	time_t timestamp;	void *data;	size_t len;	struct tmsg_list_elem *next;};/* Helper function to get a printable name for websocket opcodes */static const char *msgtypename(int flags){	unsigned f = (unsigned)flags & 0xFu;	switch (f) {	case MG_WEBSOCKET_OPCODE_CONTINUATION:		return "continuation";	case MG_WEBSOCKET_OPCODE_TEXT:		return "text";	case MG_WEBSOCKET_OPCODE_BINARY:		return "binary";	case MG_WEBSOCKET_OPCODE_CONNECTION_CLOSE:		return "connection close";	case MG_WEBSOCKET_OPCODE_PING:		return "PING";	case MG_WEBSOCKET_OPCODE_PONG:		return "PONG";	}	return "unknown";}/* Callback for handling data received from the server */static intwebsocket_client_data_handler(struct mg_connection *conn,                              int flags,                              char *data,                              size_t data_len,                              void *user_data){	struct tclient_data *pclient_data = (struct tclient_data *)user_data;	time_t now = time(NULL);	/* We may get some different message types (websocket opcodes).	 * We will handle these messages differently. */	int is_text = ((flags & 0xf) == MG_WEBSOCKET_OPCODE_TEXT);	int is_bin = ((flags & 0xf) == MG_WEBSOCKET_OPCODE_BINARY);	int is_ping = ((flags & 0xf) == MG_WEBSOCKET_OPCODE_PING);	int is_pong = ((flags & 0xf) == MG_WEBSOCKET_OPCODE_PONG);	int is_close = ((flags & 0xf) == MG_WEBSOCKET_OPCODE_CONNECTION_CLOSE);	/* Log output: We got some data */	printf("%10.0f - Client received %lu bytes of %s data from server%s",	       difftime(now, pclient_data->started),	       (long unsigned)data_len,	       msgtypename(flags),	       (is_text ? ": " : ".\n"));	/* Check if we got a websocket PING request */	if (is_ping) {		/* PING requests are to check if the connection is broken.		 * They should be replied with a PONG with the same data.		 */		mg_websocket_client_write(conn,		                          MG_WEBSOCKET_OPCODE_PONG,		                          data,		                          data_len);		return 1;	}	/* Check if we got a websocket PONG message */	if (is_pong) {		/* A PONG message may be a response to our PING, but		 * it is also allowed to send unsolicited PONG messages		 * send by the server to check some lower level TCP		 * connections. Just ignore all kinds of PONGs. */		return 1;	}	/* It we got a websocket TEXT message, handle it ... */	if (is_text) {		struct tmsg_list_elem *p;		struct tmsg_list_elem **where = &(pclient_data->msgs);		/* ... by printing it to the log ... */		fwrite(data, 1, data_len, stdout);		printf("\n");		/* ... and storing it (OOM ignored for simplicity). */		p = (struct tmsg_list_elem *)malloc(sizeof(struct tmsg_list_elem));		p->timestamp = now;		p->data = malloc(data_len);		memcpy(p->data, data, data_len);		p->len = data_len;		p->next = NULL;		while (*where != NULL) {			where = &((*where)->next);		}		*where = p;	}	/* Another option would be BINARY data. */	if (is_bin) {		/* In this example, we just ignore binary data.		 * According to some blogs, discriminating TEXT and		 * BINARY may be some remains from earlier drafts		 * of the WebSocket protocol.		 * Anyway, a real application will usually use		 * either TEXT or BINARY. */	}	/* It could be a CLOSE message as well. */	if (is_close) {		printf("%10.0f - Goodbye\n", difftime(now, pclient_data->started));		return 0;	}	/* Return 1 to keep the connection open */	return 1;}/* Callback for handling a close message received from the server */static voidwebsocket_client_close_handler(const struct mg_connection *conn,                               void *user_data){	struct tclient_data *pclient_data = (struct tclient_data *)user_data;	pclient_data->closed = time(NULL);	printf("%10.0f - Client: Close handler\n",	       difftime(pclient_data->closed, pclient_data->started));}/* Websocket client test function */voidrun_websocket_client(const char *host,                     int port,                     int secure,                     const char *path,                     const char *greetings){	char err_buf[100] = {0};	struct mg_connection *client_conn;	struct tclient_data *pclient_data;	int i;	/* Allocate some memory for callback specific data.	 * For simplicity, we ignore OOM handling in this example. */	pclient_data = (struct tclient_data *)malloc(sizeof(struct tclient_data));	/* Store start time in the private structure */	pclient_data->started = time(NULL);	pclient_data->closed = 0;	pclient_data->msgs = NULL;	/* Log first action (time = 0.0) */	printf("%10.0f - Connecting to %s:%i\n", 0.0, host, port);	/* Connect to the given WS or WSS (WS secure) server */	client_conn = mg_connect_websocket_client(host,	                                          port,	                                          secure,	                                          err_buf,	                                          sizeof(err_buf),	                                          path,	                                          NULL,	                                          websocket_client_data_handler,	                                          websocket_client_close_handler,	                                          pclient_data);	/* Check if connection is possible */	if (client_conn == NULL) {		printf("mg_connect_websocket_client error: %s\n", err_buf);		return;	}	/* Connection established */	printf("%10.0f - Connected\n", difftime(time(NULL), pclient_data->started));	/* If there are greetings to send, do it now */	if (greetings) {		printf("%10.0f - Sending greetings\n",		       difftime(time(NULL), pclient_data->started));		mg_websocket_client_write(client_conn,		                          MG_WEBSOCKET_OPCODE_TEXT,		                          greetings,		                          strlen(greetings));	}	/* Wait for some seconds */	sleep(5);	/* Does the server play "ping pong" ? */	for (i = 0; i < 5; i++) {		/* Send a PING message every 5 seconds. */		printf("%10.0f - Sending PING\n",		       difftime(time(NULL), pclient_data->started));		mg_websocket_client_write(client_conn,		                          MG_WEBSOCKET_OPCODE_PING,		                          (const char *)&i,		                          sizeof(int));		sleep(5);	}	/* Wait a while */	/* If we do not use "ping pong", the server will probably	 * close the connection with a timeout earlier. */	sleep(150);	/* Send greetings again */	if (greetings) {		printf("%10.0f - Sending greetings again\n",		       difftime(time(NULL), pclient_data->started));		mg_websocket_client_write(client_conn,		                          MG_WEBSOCKET_OPCODE_TEXT,		                          greetings,		                          strlen(greetings));	}	/* Wait for some seconds */	sleep(5);	/* Send some "song text": http://www.99-bottles-of-beer.net/ */	{		char txt[128];		int b = 99; /* start with 99 bottles */		while (b > 0) {			/* Send "b bottles" text line. */			sprintf(txt,			        "%i bottle%s of beer on the wall, "			        "%i bottle%s of beer.",			        b,			        ((b != 1) ? "s" : ""),			        b,			        ((b != 1) ? "s" : ""));			mg_websocket_client_write(client_conn,			                          MG_WEBSOCKET_OPCODE_TEXT,			                          txt,			                          strlen(txt));			/* Take a breath. */			sleep(1);			/* Drink a bottle */			b--;			/* Send "remaining bottles" text line. */			if (b) {				sprintf(txt,				        "Take one down and pass it around, "				        "%i bottle%s of beer on the wall.",				        b,				        ((b != 1) ? "s" : ""));			} else {				strcpy(txt,				       "Take one down and pass it around, "				       "no more bottles of beer on the wall.");			}			mg_websocket_client_write(client_conn,			                          MG_WEBSOCKET_OPCODE_TEXT,			                          txt,			                          strlen(txt));			/* Take a breath. */			sleep(2);		}		/* Send "no more bottles" text line. */		strcpy(txt,		       "No more bottles of beer on the wall, "		       "no more bottles of beer.");		mg_websocket_client_write(client_conn,		                          MG_WEBSOCKET_OPCODE_TEXT,		                          txt,		                          strlen(txt));		/* Take a breath. */		sleep(1);		/* Buy new bottles. */		b = 99;		/* Send "buy some more" text line. */		sprintf(txt,		        "Go to the store and buy some more, "		        "%i bottle%s of beer on the wall.",		        b,		        ((b != 1) ? "s" : ""));		mg_websocket_client_write(client_conn,		                          MG_WEBSOCKET_OPCODE_TEXT,		                          txt,		                          strlen(txt));	}	/* Wait for some seconds */	sleep(5);	/* Somewhat boring conversation, isn't it?	 * Tell the server we have to leave. */	printf("%10.0f - Sending close message\n",	       difftime(time(NULL), pclient_data->started));	mg_websocket_client_write(client_conn,	                          MG_WEBSOCKET_OPCODE_CONNECTION_CLOSE,	                          NULL,	                          0);	/* We don't need to wait, this is just to have the log timestamp	 * a second later, and to not log from the handlers and from	 * here the same time (printf to stdout is not thread-safe, but	 * adding flock or mutex or an explicit logging function makes	 * this example unnecessarily complex). */	sleep(5);	/* Connection should be closed by now. */	printf("%10.0f - Connection state: %s\n",	       difftime(time(NULL), pclient_data->started),	       ((pclient_data->closed == 0) ? "open" : "closed"));	/* Close client connection */	mg_close_connection(client_conn);	printf("%10.0f - End of test\n",	       difftime(time(NULL), pclient_data->started));	/* Print collected data */	printf("\n\nPrint all text messages from server again:\n");	{		struct tmsg_list_elem **where = &(pclient_data->msgs);		while (*where != NULL) {			printf("%10.0f - [%5lu] ",			       difftime((*where)->timestamp, pclient_data->started),			       (unsigned long)(*where)->len);			fwrite((const char *)(*where)->data, 1, (*where)->len, stdout);			printf("\n");			where = &((*where)->next);		}	}	/* Free collected data */	{		struct tmsg_list_elem **where = &(pclient_data->msgs);		void *p1 = 0;		void *p2 = 0;		while (*where != NULL) {			free((*where)->data);			free(p2);			p2 = p1;			p1 = *where;			where = &((*where)->next);		}		free(p2);		free(p1);	}	free(pclient_data);}/* main will initialize the CivetWeb library * and start the WebSocket client test function */intmain(int argc, char *argv[]){	const char *greetings = "Hello World!";	const char *host = "echo.websocket.org";	const char *path = "/";#if defined(NO_SSL)	const int port = 80;	const int secure = 0;	mg_init_library(0);#else	const int port = 443;	const int secure = 1;	mg_init_library(MG_FEATURES_SSL);#endif	run_websocket_client(host, port, secure, path, greetings);}
 |