ws_server.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. // Copyright (c) 2004-2012 Sergey Lyubka
  2. // This file is a part of civetweb project, http://github.com/sunsetbrew/civetweb
  3. //
  4. // v 0.1 Contributed by William Greathouse 9-Sep-2013
  5. #include <stdio.h>
  6. #include <string.h>
  7. #include <time.h>
  8. #include <unistd.h>
  9. #include "civetweb.h"
  10. // simple structure for keeping track of websocket connection
  11. struct ws_connection {
  12. struct mg_connection *conn;
  13. int update;
  14. int closing;
  15. };
  16. // time base and structure periodic updates to client for demo
  17. #define BASETIME 100000 /* 0.1 seconds */
  18. struct progress {
  19. int limit;
  20. int increment;
  21. int period;
  22. int value;
  23. };
  24. // up to 16 independent client connections
  25. #define CONNECTIONS 16
  26. static struct ws_connection ws_conn[CONNECTIONS];
  27. // ws_server_thread()
  28. // Simple demo server thread. Sends periodic updates to connected clients
  29. static void *ws_server_thread(void *parm)
  30. {
  31. int wsd = (long)parm;
  32. struct mg_connection *conn = ws_conn[wsd].conn;
  33. int timer = 0;
  34. char tstr[32];
  35. int i;
  36. struct progress meter[] = {
  37. /* first meter 0 to 1000, by 5 every 0.1 second */
  38. { 1000, 5, 1, 0 },
  39. /* second meter 0 to 500, by 10 every 0.5 second */
  40. { 500, 10, 5, 0 },
  41. /* third meter 0 to 100, by 10 every 1.0 second */
  42. { 100, 10, 10, 0},
  43. /* end of list */
  44. { 0, 0, 0, 0}
  45. };
  46. fprintf(stderr, "ws_server_thread %d\n", wsd);
  47. /* Send initial meter updates */
  48. for (i=0; meter[i].period != 0; i++)
  49. {
  50. if (meter[i].value >= meter[i].limit)
  51. meter[i].value = 0;
  52. if (meter[i].value >= meter[i].limit)
  53. meter[i].value = meter[i].limit;
  54. sprintf(tstr, "meter%d:%d,%d", i+1,
  55. meter[i].value, meter[i].limit);
  56. mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, tstr, strlen(tstr));
  57. }
  58. /* While the connection is open, send periodic updates */
  59. while(!ws_conn[wsd].closing)
  60. {
  61. usleep(100000); /* 0.1 second */
  62. timer++;
  63. /* Send meter updates */
  64. if (ws_conn[wsd].update)
  65. {
  66. for (i=0; meter[i].period != 0; i++)
  67. {
  68. if (timer%meter[i].period == 0)
  69. {
  70. if (meter[i].value >= meter[i].limit)
  71. meter[i].value = 0;
  72. else
  73. meter[i].value += meter[i].increment;
  74. if (meter[i].value >= meter[i].limit)
  75. meter[i].value = meter[i].limit;
  76. // if we are closing, server should not send new data
  77. if (!ws_conn[wsd].closing)
  78. {
  79. sprintf(tstr, "meter%d:%d,%d", i+1,
  80. meter[i].value, meter[i].limit);
  81. mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, tstr, strlen(tstr));
  82. }
  83. }
  84. }
  85. }
  86. /* Send periodic PING to assure websocket remains connected, except if we are closing */
  87. if (timer%100 == 0 && !ws_conn[wsd].closing)
  88. mg_websocket_write(conn, WEBSOCKET_OPCODE_PING, NULL, 0);
  89. }
  90. fprintf(stderr, "ws_server_thread %d exiting\n", wsd);
  91. // reset connection information to allow reuse by new client
  92. ws_conn[wsd].conn = NULL;
  93. ws_conn[wsd].update = 0;
  94. ws_conn[wsd].closing = 2;
  95. return NULL;
  96. }
  97. // websocket_connect_handler()
  98. // On new client connection, find next available server connection and store
  99. // new connection information. If no more server connections are available
  100. // tell civetweb to not accept the client request.
  101. static int websocket_connect_handler(const struct mg_connection *conn) {
  102. int i;
  103. fprintf(stderr, "connect handler\n");
  104. for(i=0; i < CONNECTIONS; ++i)
  105. {
  106. if (ws_conn[i].conn == NULL)
  107. {
  108. fprintf(stderr, "...prep for server %d\n", i);
  109. ws_conn[i].conn = (struct mg_connection *)conn;
  110. ws_conn[i].closing = 0;
  111. ws_conn[i].update = 0;
  112. break;
  113. }
  114. }
  115. if (i >= CONNECTIONS)
  116. {
  117. fprintf(stderr, "Refused connection: Max connections exceeded\n");
  118. return 1;
  119. }
  120. return 0;
  121. }
  122. // websocket_ready_handler()
  123. // Once websocket negotiation is complete, start a server for the connection
  124. static void websocket_ready_handler(struct mg_connection *conn) {
  125. int i;
  126. fprintf(stderr, "ready handler\n");
  127. for(i=0; i < CONNECTIONS; ++i)
  128. {
  129. if (ws_conn[i].conn == conn)
  130. {
  131. fprintf(stderr, "...start server %d\n", i);
  132. mg_start_thread(ws_server_thread, (void *)(long)i);
  133. break;
  134. }
  135. }
  136. }
  137. // websocket_close_handler()
  138. // When websocket is closed, tell the associated server to shut down
  139. static void websocket_close_handler(struct mg_connection *conn) {
  140. int i;
  141. //fprintf(stderr, "close handler\n"); /* called for every close, not just websockets */
  142. for(i=0; i < CONNECTIONS; ++i)
  143. {
  144. if (ws_conn[i].conn == conn)
  145. {
  146. fprintf(stderr, "...close server %d\n", i);
  147. ws_conn[i].closing = 1;
  148. }
  149. }
  150. }
  151. // Arguments:
  152. // flags: first byte of websocket frame, see websocket RFC,
  153. // http://tools.ietf.org/html/rfc6455, section 5.2
  154. // data, data_len: payload data. Mask, if any, is already applied.
  155. static int websocket_data_handler(struct mg_connection *conn, int flags,
  156. char *data, size_t data_len) {
  157. int i;
  158. int wsd;
  159. for(i=0; i < CONNECTIONS; ++i)
  160. {
  161. if (ws_conn[i].conn == conn)
  162. {
  163. wsd = i;
  164. break;
  165. }
  166. }
  167. if (i >= CONNECTIONS)
  168. {
  169. fprintf(stderr, "Received websocket data from unknown connection\n");
  170. return 1;
  171. }
  172. if (flags & 0x80)
  173. {
  174. flags &= 0x7f;
  175. switch (flags)
  176. {
  177. case WEBSOCKET_OPCODE_CONTINUATION:
  178. fprintf(stderr, "CONTINUATION...\n");
  179. break;
  180. case WEBSOCKET_OPCODE_TEXT:
  181. fprintf(stderr, "TEXT: %-.*s\n", (int)data_len, data);
  182. /*** interpret data as commands here ***/
  183. if (strncmp("update on", data, data_len)== 0)
  184. {
  185. /* turn on updates */
  186. ws_conn[wsd].update = 1;
  187. /* echo back */
  188. mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, data, data_len);
  189. }
  190. else if (strncmp("update off", data, data_len)== 0)
  191. {
  192. /* turn off updates */
  193. ws_conn[wsd].update = 0;
  194. /* echo back */
  195. mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, data, data_len);
  196. }
  197. break;
  198. case WEBSOCKET_OPCODE_BINARY:
  199. fprintf(stderr, "BINARY...\n");
  200. break;
  201. case WEBSOCKET_OPCODE_CONNECTION_CLOSE:
  202. fprintf(stderr, "CLOSE...\n");
  203. /* If client initiated close, respond with close message in acknowlegement */
  204. if (!ws_conn[wsd].closing)
  205. {
  206. mg_websocket_write(conn, WEBSOCKET_OPCODE_CONNECTION_CLOSE, data, data_len);
  207. ws_conn[wsd].closing = 1; /* we should not send addional messages when close requested/acknowledged */
  208. }
  209. return 0; /* time to close the connection */
  210. break;
  211. case WEBSOCKET_OPCODE_PING:
  212. /* client sent PING, respond with PONG */
  213. mg_websocket_write(conn, WEBSOCKET_OPCODE_PONG, data, data_len);
  214. break;
  215. case WEBSOCKET_OPCODE_PONG:
  216. /* received PONG to our PING, no action */
  217. break;
  218. default:
  219. fprintf(stderr, "Unknown flags: %02x\n", flags);
  220. break;
  221. }
  222. }
  223. return 1; /* keep connection open */
  224. }
  225. int main(void)
  226. {
  227. char server_name[40];
  228. struct mg_context *ctx;
  229. struct mg_callbacks callbacks;
  230. const char *options[] = {
  231. "listening_ports", "8080",
  232. "document_root", "docroot",
  233. NULL
  234. };
  235. /* get simple greeting for the web server */
  236. snprintf(server_name, sizeof(server_name),
  237. "Civetweb websocket server v. %s",
  238. mg_version());
  239. memset(&callbacks, 0, sizeof(callbacks));
  240. callbacks.websocket_connect = websocket_connect_handler;
  241. callbacks.websocket_ready = websocket_ready_handler;
  242. callbacks.websocket_data = websocket_data_handler;
  243. callbacks.connection_close = websocket_close_handler;
  244. ctx = mg_start(&callbacks, NULL, options);
  245. /* show the greeting and some basic information */
  246. printf("%s started on port(s) %s with web root [%s]\n",
  247. server_name, mg_get_option(ctx, "listening_ports"),
  248. mg_get_option(ctx, "document_root"));
  249. getchar(); // Wait until user hits "enter"
  250. mg_stop(ctx);
  251. return 0;
  252. }