1 // Copyright (c) 2004-2012 Sergey Lyubka
2 // This file is a part of civetweb project, http://github.com/bel2125/civetweb
4 // v 0.1 Contributed by William Greathouse 9-Sep-2013
13 // simple structure for keeping track of websocket connection
14 struct ws_connection
{
15 struct mg_connection
*conn
;
20 // time base and structure periodic updates to client for demo
21 #define BASETIME 100000 /* 0.1 seconds */
29 // up to 16 independent client connections
30 #define CONNECTIONS 16
31 static struct ws_connection ws_conn
[CONNECTIONS
];
35 // Simple demo server thread. Sends periodic updates to connected clients
36 static void *ws_server_thread(void *parm
)
39 struct mg_connection
*conn
= ws_conn
[wsd
].conn
;
43 struct progress meter
[] = {
44 /* first meter 0 to 1000, by 5 every 0.1 second */
46 /* second meter 0 to 500, by 10 every 0.5 second */
48 /* third meter 0 to 100, by 10 every 1.0 second */
54 fprintf(stderr
, "ws_server_thread %d\n", wsd
);
56 /* Send initial meter updates */
57 for (i
=0; meter
[i
].period
!= 0; i
++) {
58 if (meter
[i
].value
>= meter
[i
].limit
)
60 if (meter
[i
].value
>= meter
[i
].limit
)
61 meter
[i
].value
= meter
[i
].limit
;
62 sprintf(tstr
, "meter%d:%d,%d", i
+1,
63 meter
[i
].value
, meter
[i
].limit
);
64 mg_websocket_write(conn
, WEBSOCKET_OPCODE_TEXT
, tstr
, strlen(tstr
));
67 /* While the connection is open, send periodic updates */
68 while(!ws_conn
[wsd
].closing
) {
69 usleep(100000); /* 0.1 second */
72 /* Send meter updates */
73 if (ws_conn
[wsd
].update
) {
74 for (i
=0; meter
[i
].period
!= 0; i
++) {
75 if (timer
%meter
[i
].period
== 0) {
76 if (meter
[i
].value
>= meter
[i
].limit
)
79 meter
[i
].value
+= meter
[i
].increment
;
80 if (meter
[i
].value
>= meter
[i
].limit
)
81 meter
[i
].value
= meter
[i
].limit
;
82 // if we are closing, server should not send new data
83 if (!ws_conn
[wsd
].closing
) {
84 sprintf(tstr
, "meter%d:%d,%d", i
+1,
85 meter
[i
].value
, meter
[i
].limit
);
86 mg_websocket_write(conn
, WEBSOCKET_OPCODE_TEXT
, tstr
, strlen(tstr
));
92 /* Send periodic PING to assure websocket remains connected, except if we are closing */
93 if (timer
%100 == 0 && !ws_conn
[wsd
].closing
)
94 mg_websocket_write(conn
, WEBSOCKET_OPCODE_PING
, NULL
, 0);
97 fprintf(stderr
, "ws_server_thread %d exiting\n", wsd
);
99 // reset connection information to allow reuse by new client
100 ws_conn
[wsd
].conn
= NULL
;
101 ws_conn
[wsd
].update
= 0;
102 ws_conn
[wsd
].closing
= 2;
107 // websocket_connect_handler()
108 // On new client connection, find next available server connection and store
109 // new connection information. If no more server connections are available
110 // tell civetweb to not accept the client request.
111 static int websocket_connect_handler(const struct mg_connection
*conn
)
115 fprintf(stderr
, "connect handler\n");
117 for(i
=0; i
< CONNECTIONS
; ++i
) {
118 if (ws_conn
[i
].conn
== NULL
) {
119 fprintf(stderr
, "...prep for server %d\n", i
);
120 ws_conn
[i
].conn
= (struct mg_connection
*)conn
;
121 ws_conn
[i
].closing
= 0;
122 ws_conn
[i
].update
= 0;
126 if (i
>= CONNECTIONS
) {
127 fprintf(stderr
, "Refused connection: Max connections exceeded\n");
134 // websocket_ready_handler()
135 // Once websocket negotiation is complete, start a server for the connection
136 static void websocket_ready_handler(struct mg_connection
*conn
)
140 fprintf(stderr
, "ready handler\n");
142 for(i
=0; i
< CONNECTIONS
; ++i
) {
143 if (ws_conn
[i
].conn
== conn
) {
144 fprintf(stderr
, "...start server %d\n", i
);
145 mg_start_thread(ws_server_thread
, (void *)(long)i
);
151 // websocket_close_handler()
152 // When websocket is closed, tell the associated server to shut down
153 static void websocket_close_handler(struct mg_connection
*conn
)
157 //fprintf(stderr, "close handler\n"); /* called for every close, not just websockets */
159 for(i
=0; i
< CONNECTIONS
; ++i
) {
160 if (ws_conn
[i
].conn
== conn
) {
161 fprintf(stderr
, "...close server %d\n", i
);
162 ws_conn
[i
].closing
= 1;
168 // flags: first byte of websocket frame, see websocket RFC,
169 // http://tools.ietf.org/html/rfc6455, section 5.2
170 // data, data_len: payload data. Mask, if any, is already applied.
171 static int websocket_data_handler(struct mg_connection
*conn
, int flags
,
172 char *data
, size_t data_len
)
177 for(i
=0; i
< CONNECTIONS
; ++i
) {
178 if (ws_conn
[i
].conn
== conn
) {
183 if (i
>= CONNECTIONS
) {
184 fprintf(stderr
, "Received websocket data from unknown connection\n");
191 case WEBSOCKET_OPCODE_CONTINUATION
:
192 fprintf(stderr
, "CONTINUATION...\n");
194 case WEBSOCKET_OPCODE_TEXT
:
195 fprintf(stderr
, "TEXT: %-.*s\n", (int)data_len
, data
);
196 /*** interpret data as commands here ***/
197 if (strncmp("update on", data
, data_len
)== 0) {
198 /* turn on updates */
199 ws_conn
[wsd
].update
= 1;
201 mg_websocket_write(conn
, WEBSOCKET_OPCODE_TEXT
, data
, data_len
);
202 } else if (strncmp("update off", data
, data_len
)== 0) {
203 /* turn off updates */
204 ws_conn
[wsd
].update
= 0;
206 mg_websocket_write(conn
, WEBSOCKET_OPCODE_TEXT
, data
, data_len
);
209 case WEBSOCKET_OPCODE_BINARY
:
210 fprintf(stderr
, "BINARY...\n");
212 case WEBSOCKET_OPCODE_CONNECTION_CLOSE
:
213 fprintf(stderr
, "CLOSE...\n");
214 /* If client initiated close, respond with close message in acknowlegement */
215 if (!ws_conn
[wsd
].closing
) {
216 mg_websocket_write(conn
, WEBSOCKET_OPCODE_CONNECTION_CLOSE
, data
, data_len
);
217 ws_conn
[wsd
].closing
= 1; /* we should not send addional messages when close requested/acknowledged */
219 return 0; /* time to close the connection */
221 case WEBSOCKET_OPCODE_PING
:
222 /* client sent PING, respond with PONG */
223 mg_websocket_write(conn
, WEBSOCKET_OPCODE_PONG
, data
, data_len
);
225 case WEBSOCKET_OPCODE_PONG
:
226 /* received PONG to our PING, no action */
229 fprintf(stderr
, "Unknown flags: %02x\n", flags
);
234 return 1; /* keep connection open */
240 char server_name
[40];
241 struct mg_context
*ctx
;
242 struct mg_callbacks callbacks
;
243 const char *options
[] = {
244 "listening_ports", "8080",
245 "document_root", "docroot",
249 /* get simple greeting for the web server */
250 snprintf(server_name
, sizeof(server_name
),
251 "Civetweb websocket server v. %s",
254 memset(&callbacks
, 0, sizeof(callbacks
));
255 callbacks
.websocket_connect
= websocket_connect_handler
;
256 callbacks
.websocket_ready
= websocket_ready_handler
;
257 callbacks
.websocket_data
= websocket_data_handler
;
258 callbacks
.connection_close
= websocket_close_handler
;
260 ctx
= mg_start(&callbacks
, NULL
, options
);
262 /* show the greeting and some basic information */
263 printf("%s started on port(s) %s with web root [%s]\n",
264 server_name
, mg_get_option(ctx
, "listening_ports"),
265 mg_get_option(ctx
, "document_root"));
267 getchar(); // Wait until user hits "enter"