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 int
- websocket_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 void
- websocket_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 */
- void
- run_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 */
- int
- main(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);
- }
|