mirror of
https://github.com/lammertb/libhttp.git
synced 2025-08-06 05:02:40 +03:00
Add WebSocket demo that uses asynchronous messages from the server.
This commit is contained in:
committed by
Thomas Davis
parent
aa51d80d24
commit
aa7c2a1b8c
36
examples/ws_server/Makefile
Normal file
36
examples/ws_server/Makefile
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2013 No Face Press, LLC
|
||||||
|
# License http://opensource.org/licenses/mit-license.php MIT License
|
||||||
|
#
|
||||||
|
|
||||||
|
#This makefile is used to test the other Makefiles
|
||||||
|
|
||||||
|
|
||||||
|
PROG = ws_server
|
||||||
|
SRC = ws_server.c
|
||||||
|
|
||||||
|
TOP = ../..
|
||||||
|
CIVETWEB_LIB = libcivetweb.a
|
||||||
|
|
||||||
|
CFLAGS = -I$(TOP)/include $(COPT)
|
||||||
|
LIBS = -lpthread
|
||||||
|
|
||||||
|
include $(TOP)/resources/Makefile.in-os
|
||||||
|
|
||||||
|
ifeq ($(TARGET_OS),LINUX)
|
||||||
|
LIBS += -ldl
|
||||||
|
endif
|
||||||
|
|
||||||
|
all: $(PROG)
|
||||||
|
|
||||||
|
$(PROG): $(CIVETWEB_LIB) $(SRC)
|
||||||
|
$(CC) -o $@ $(CFLAGS) $(LDFLAGS) $(SRC) $(CIVETWEB_LIB) $(LIBS)
|
||||||
|
|
||||||
|
$(CIVETWEB_LIB):
|
||||||
|
$(MAKE) -C $(TOP) clean lib WITH_WEBSOCKET=1
|
||||||
|
cp $(TOP)/$(CIVETWEB_LIB) .
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(CIVETWEB_LIB) $(PROG)
|
||||||
|
|
||||||
|
.PHONY: all clean
|
316
examples/ws_server/docroot/index.html
Normal file
316
examples/ws_server/docroot/index.html
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset='UTF-8'>
|
||||||
|
|
||||||
|
<title>Websocket Meters</title>
|
||||||
|
<!--
|
||||||
|
Version 0.1 Contributed by William Greathouse 9-Sep-2013
|
||||||
|
Simple demo of WebSocket connection use. Not a great example of web coding,
|
||||||
|
but it is functional.
|
||||||
|
|
||||||
|
The meter displays are adapted from CSS-TRICKS Progress Bars by Chris Coyier
|
||||||
|
at http://css-tricks.com/css3-progress-bars/
|
||||||
|
-->
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background: #222;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
color: white;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
width: 225px;
|
||||||
|
height: 30px;
|
||||||
|
margin: auto 10px;
|
||||||
|
background-color: #ccc;
|
||||||
|
-moz-border-radius: 5px;
|
||||||
|
-webkit-border-radius: 5px;
|
||||||
|
border-radius:6px;
|
||||||
|
color: blue;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
button:hover {
|
||||||
|
background-color: #888;
|
||||||
|
}
|
||||||
|
button:hover:disabled {
|
||||||
|
background-color: #ccc;
|
||||||
|
}
|
||||||
|
button:disabled {
|
||||||
|
color: lightgray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button_container {
|
||||||
|
width:550px;
|
||||||
|
display:block;
|
||||||
|
margin-left:auto;
|
||||||
|
margin-right:auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meter {
|
||||||
|
height: 20px; /* Can be anything */
|
||||||
|
position: relative;
|
||||||
|
background: #555;
|
||||||
|
-moz-border-radius: 25px;
|
||||||
|
-webkit-border-radius: 25px;
|
||||||
|
border-radius: 25px;
|
||||||
|
padding: 10px;
|
||||||
|
-webkit-box-shadow: inset 0 -1px 1px rgba(255,255,255,0.3);
|
||||||
|
-moz-box-shadow : inset 0 -1px 1px rgba(255,255,255,0.3);
|
||||||
|
box-shadow : inset 0 -1px 1px rgba(255,255,255,0.3);
|
||||||
|
}
|
||||||
|
.meter > span {
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
-webkit-border-top-right-radius: 20px;
|
||||||
|
-webkit-border-bottom-right-radius: 20px;
|
||||||
|
-moz-border-radius-topright: 20px;
|
||||||
|
-moz-border-radius-bottomright: 20px;
|
||||||
|
border-top-right-radius: 20px;
|
||||||
|
border-bottom-right-radius: 20px;
|
||||||
|
-webkit-border-top-left-radius: 20px;
|
||||||
|
-webkit-border-bottom-left-radius: 20px;
|
||||||
|
-moz-border-radius-topleft: 20px;
|
||||||
|
-moz-border-radius-bottomleft: 20px;
|
||||||
|
border-top-left-radius: 20px;
|
||||||
|
border-bottom-left-radius: 20px;
|
||||||
|
background-color: rgb(43,194,83);
|
||||||
|
background-image: -webkit-gradient(
|
||||||
|
linear,
|
||||||
|
left bottom,
|
||||||
|
left top,
|
||||||
|
color-stop(0, rgb(43,194,83)),
|
||||||
|
color-stop(1, rgb(84,240,84))
|
||||||
|
);
|
||||||
|
background-image: -moz-linear-gradient(
|
||||||
|
center bottom,
|
||||||
|
rgb(43,194,83) 37%,
|
||||||
|
rgb(84,240,84) 69%
|
||||||
|
);
|
||||||
|
-webkit-box-shadow:
|
||||||
|
inset 0 2px 9px rgba(255,255,255,0.3),
|
||||||
|
inset 0 -2px 6px rgba(0,0,0,0.4);
|
||||||
|
-moz-box-shadow:
|
||||||
|
inset 0 2px 9px rgba(255,255,255,0.3),
|
||||||
|
inset 0 -2px 6px rgba(0,0,0,0.4);
|
||||||
|
box-shadow:
|
||||||
|
inset 0 2px 9px rgba(255,255,255,0.3),
|
||||||
|
inset 0 -2px 6px rgba(0,0,0,0.4);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.meter > span:after, .animate > span > span {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0; left: 0; bottom: 0; right: 0;
|
||||||
|
background-image:
|
||||||
|
-webkit-gradient(linear, 0 0, 100% 100%,
|
||||||
|
color-stop(.25, rgba(255, 255, 255, .2)),
|
||||||
|
color-stop(.25, transparent), color-stop(.5, transparent),
|
||||||
|
color-stop(.5, rgba(255, 255, 255, .2)),
|
||||||
|
color-stop(.75, rgba(255, 255, 255, .2)),
|
||||||
|
color-stop(.75, transparent), to(transparent)
|
||||||
|
);
|
||||||
|
background-image:
|
||||||
|
-moz-linear-gradient(
|
||||||
|
-45deg,
|
||||||
|
rgba(255, 255, 255, .2) 25%,
|
||||||
|
transparent 25%,
|
||||||
|
transparent 50%,
|
||||||
|
rgba(255, 255, 255, .2) 50%,
|
||||||
|
rgba(255, 255, 255, .2) 75%,
|
||||||
|
transparent 75%,
|
||||||
|
transparent
|
||||||
|
);
|
||||||
|
z-index: 1;
|
||||||
|
-webkit-background-size: 50px 50px;
|
||||||
|
-moz-background-size: 50px 50px;
|
||||||
|
-webkit-animation: move 2s linear infinite;
|
||||||
|
-webkit-border-top-right-radius: 20px;
|
||||||
|
-webkit-border-bottom-right-radius: 20px;
|
||||||
|
-moz-border-radius-topright: 20px;
|
||||||
|
-moz-border-radius-bottomright: 20px;
|
||||||
|
border-top-right-radius: 20px;
|
||||||
|
border-bottom-right-radius: 20px;
|
||||||
|
-webkit-border-top-left-radius: 20px;
|
||||||
|
-webkit-border-bottom-left-radius: 20px;
|
||||||
|
-moz-border-radius-topleft: 20px;
|
||||||
|
-moz-border-radius-bottomleft: 20px;
|
||||||
|
border-top-left-radius: 20px;
|
||||||
|
border-bottom-left-radius: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate > span:after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes move {
|
||||||
|
0% {
|
||||||
|
background-position: 0 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position: 50px 50px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.orange > span {
|
||||||
|
background-color: #f1a165;
|
||||||
|
background-image: -moz-linear-gradient(top, #f1a165, #f36d0a);
|
||||||
|
background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #f1a165),color-stop(1, #f36d0a));
|
||||||
|
background-image: -webkit-linear-gradient(#f1a165, #f36d0a);
|
||||||
|
}
|
||||||
|
|
||||||
|
.red > span {
|
||||||
|
background-color: #f0a3a3;
|
||||||
|
background-image: -moz-linear-gradient(top, #f0a3a3, #f42323);
|
||||||
|
background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #f0a3a3),color-stop(1, #f42323));
|
||||||
|
background-image: -webkit-linear-gradient(#f0a3a3, #f42323);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nostripes > span > span, .nostripes > span:after {
|
||||||
|
-webkit-animation: none;
|
||||||
|
background-image: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#output {
|
||||||
|
background-color: #ccc;
|
||||||
|
height: 240px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="page_wrap">
|
||||||
|
|
||||||
|
<h1>Meter Updates via WebSocket</h1>
|
||||||
|
|
||||||
|
<p/>
|
||||||
|
|
||||||
|
<div class="meter">
|
||||||
|
<span id="meter1" style="width: 25%"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p/>
|
||||||
|
|
||||||
|
<div class="meter orange nostripes">
|
||||||
|
<span id="meter2" style="width: 33.3%"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p/>
|
||||||
|
|
||||||
|
<div class="meter red">
|
||||||
|
<span id="meter3" style="width: 80%"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="button_container">
|
||||||
|
<div>
|
||||||
|
<button id="connection" onclick="toggleConnection(this)">WebSocket Connect</button>
|
||||||
|
<button id="update" disabled onclick="toggleUpdate(this)">Disable Update</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p/>
|
||||||
|
<div id="output"></div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<script language="javascript" type="text/javascript">
|
||||||
|
var connection; // websocket connection
|
||||||
|
|
||||||
|
function writeToScreen (message) {
|
||||||
|
var div = document.createElement('div');
|
||||||
|
var output = document.getElementById('output');
|
||||||
|
div.innerHTML = message;
|
||||||
|
output.appendChild(div);
|
||||||
|
output.scrollTop = output.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ws_connect() {
|
||||||
|
// check for websocket support
|
||||||
|
// for Internet Explorer < 10 there are options for websocket support that
|
||||||
|
// could be integrated into production code, but for now, we are expecting
|
||||||
|
// browser support to be present for this demo
|
||||||
|
if ('WebSocket' in window) {
|
||||||
|
|
||||||
|
writeToScreen('Connecting');
|
||||||
|
connection = new WebSocket('ws://' + window.location.host + '/meters');
|
||||||
|
connection.onopen = function(ev) {
|
||||||
|
document.getElementById("connection").innerHTML = "WebSocket Disconnect";
|
||||||
|
document.getElementById("update").disabled=false;
|
||||||
|
document.getElementById("update").innerHTML = "Disable Update";
|
||||||
|
writeToScreen('CONNECTED');
|
||||||
|
var message = 'update on';
|
||||||
|
writeToScreen('SENT: ' + message);
|
||||||
|
connection.send(message);
|
||||||
|
};
|
||||||
|
|
||||||
|
connection.onclose = function(ev) {
|
||||||
|
document.getElementById("update").disabled=true;
|
||||||
|
document.getElementById("update").innerHTML = "Enable Update";
|
||||||
|
document.getElementById("connection").innerHTML = "WebSocket Connect";
|
||||||
|
writeToScreen('DISCONNECTED');
|
||||||
|
};
|
||||||
|
|
||||||
|
connection.onmessage = function(ev) {
|
||||||
|
if (ev.data.substr(0,5) == "meter")
|
||||||
|
{
|
||||||
|
var target = ev.data.split(":")[0];
|
||||||
|
var meter = document.getElementById(target);
|
||||||
|
var data = ev.data.split(":")[1].split(",");
|
||||||
|
var percent = (data[0]*100)/data[1];
|
||||||
|
meter.style.width = percent+"%";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
writeToScreen('RECEIVED: ' + ev.data);
|
||||||
|
};
|
||||||
|
|
||||||
|
connection.onerror = function(ev) {
|
||||||
|
alert("WebSocket error");
|
||||||
|
};
|
||||||
|
|
||||||
|
} else {
|
||||||
|
alert("WebSocket is not available!!!\n" +
|
||||||
|
"Demo will not function.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// user connect/disconnect
|
||||||
|
function toggleConnection(el) {
|
||||||
|
var tag=el.innerHTML;
|
||||||
|
if (tag == "WebSocket Connect")
|
||||||
|
{
|
||||||
|
ws_connect();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
connection.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// user turn updates on/off
|
||||||
|
function toggleUpdate(el) {
|
||||||
|
var tag=el.innerHTML;
|
||||||
|
var message;
|
||||||
|
if (tag == "Enable Update")
|
||||||
|
{
|
||||||
|
message = 'update on';
|
||||||
|
el.innerHTML = "Disable Update";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
message = 'update off';
|
||||||
|
el.innerHTML = "Enable Update";
|
||||||
|
}
|
||||||
|
writeToScreen('SENT: ' + message);
|
||||||
|
connection.send(message);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</html>
|
289
examples/ws_server/ws_server.c
Normal file
289
examples/ws_server/ws_server.c
Normal file
@@ -0,0 +1,289 @@
|
|||||||
|
// Copyright (c) 2004-2012 Sergey Lyubka
|
||||||
|
// This file is a part of civetweb project, http://github.com/sunsetbrew/civetweb
|
||||||
|
//
|
||||||
|
// v 0.1 Contributed by William Greathouse 9-Sep-2013
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "civetweb.h"
|
||||||
|
|
||||||
|
// simple structure for keeping track of websocket connection
|
||||||
|
struct ws_connection {
|
||||||
|
struct mg_connection *conn;
|
||||||
|
int update;
|
||||||
|
int closing;
|
||||||
|
};
|
||||||
|
|
||||||
|
// time base and structure periodic updates to client for demo
|
||||||
|
#define BASETIME 100000 /* 0.1 seconds */
|
||||||
|
struct progress {
|
||||||
|
int limit;
|
||||||
|
int increment;
|
||||||
|
int period;
|
||||||
|
int value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// up to 16 independent client connections
|
||||||
|
#define CONNECTIONS 16
|
||||||
|
static struct ws_connection ws_conn[CONNECTIONS];
|
||||||
|
|
||||||
|
|
||||||
|
// ws_server_thread()
|
||||||
|
// Simple demo server thread. Sends periodic updates to connected clients
|
||||||
|
static void *ws_server_thread(void *parm)
|
||||||
|
{
|
||||||
|
int wsd = (long)parm;
|
||||||
|
struct mg_connection *conn = ws_conn[wsd].conn;
|
||||||
|
int timer = 0;
|
||||||
|
char tstr[32];
|
||||||
|
int i;
|
||||||
|
struct progress meter[] = {
|
||||||
|
/* first meter 0 to 1000, by 5 every 0.1 second */
|
||||||
|
{ 1000, 5, 1, 0 },
|
||||||
|
/* second meter 0 to 500, by 10 every 0.5 second */
|
||||||
|
{ 500, 10, 5, 0 },
|
||||||
|
/* third meter 0 to 100, by 10 every 1.0 second */
|
||||||
|
{ 100, 10, 10, 0},
|
||||||
|
/* end of list */
|
||||||
|
{ 0, 0, 0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
fprintf(stderr, "ws_server_thread %d\n", wsd);
|
||||||
|
|
||||||
|
/* Send initial meter updates */
|
||||||
|
for (i=0; meter[i].period != 0; i++)
|
||||||
|
{
|
||||||
|
if (meter[i].value >= meter[i].limit)
|
||||||
|
meter[i].value = 0;
|
||||||
|
if (meter[i].value >= meter[i].limit)
|
||||||
|
meter[i].value = meter[i].limit;
|
||||||
|
sprintf(tstr, "meter%d:%d,%d", i+1,
|
||||||
|
meter[i].value, meter[i].limit);
|
||||||
|
mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, tstr, strlen(tstr));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* While the connection is open, send periodic updates */
|
||||||
|
while(!ws_conn[wsd].closing)
|
||||||
|
{
|
||||||
|
usleep(100000); /* 0.1 second */
|
||||||
|
timer++;
|
||||||
|
|
||||||
|
/* Send meter updates */
|
||||||
|
if (ws_conn[wsd].update)
|
||||||
|
{
|
||||||
|
for (i=0; meter[i].period != 0; i++)
|
||||||
|
{
|
||||||
|
if (timer%meter[i].period == 0)
|
||||||
|
{
|
||||||
|
if (meter[i].value >= meter[i].limit)
|
||||||
|
meter[i].value = 0;
|
||||||
|
else
|
||||||
|
meter[i].value += meter[i].increment;
|
||||||
|
if (meter[i].value >= meter[i].limit)
|
||||||
|
meter[i].value = meter[i].limit;
|
||||||
|
// if we are closing, server should not send new data
|
||||||
|
if (!ws_conn[wsd].closing)
|
||||||
|
{
|
||||||
|
sprintf(tstr, "meter%d:%d,%d", i+1,
|
||||||
|
meter[i].value, meter[i].limit);
|
||||||
|
mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, tstr, strlen(tstr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send periodic PING to assure websocket remains connected, except if we are closing */
|
||||||
|
if (timer%100 == 0 && !ws_conn[wsd].closing)
|
||||||
|
mg_websocket_write(conn, WEBSOCKET_OPCODE_PING, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "ws_server_thread %d exiting\n", wsd);
|
||||||
|
|
||||||
|
// reset connection information to allow reuse by new client
|
||||||
|
ws_conn[wsd].conn = NULL;
|
||||||
|
ws_conn[wsd].update = 0;
|
||||||
|
ws_conn[wsd].closing = 2;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// websocket_connect_handler()
|
||||||
|
// On new client connection, find next available server connection and store
|
||||||
|
// new connection information. If no more server connections are available
|
||||||
|
// tell civetweb to not accept the client request.
|
||||||
|
static int websocket_connect_handler(const struct mg_connection *conn) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
fprintf(stderr, "connect handler\n");
|
||||||
|
|
||||||
|
for(i=0; i < CONNECTIONS; ++i)
|
||||||
|
{
|
||||||
|
if (ws_conn[i].conn == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "...prep for server %d\n", i);
|
||||||
|
ws_conn[i].conn = (struct mg_connection *)conn;
|
||||||
|
ws_conn[i].closing = 0;
|
||||||
|
ws_conn[i].update = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i >= CONNECTIONS)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Refused connection: Max connections exceeded\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// websocket_ready_handler()
|
||||||
|
// Once websocket negotiation is complete, start a server for the connection
|
||||||
|
static void websocket_ready_handler(struct mg_connection *conn) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
fprintf(stderr, "ready handler\n");
|
||||||
|
|
||||||
|
for(i=0; i < CONNECTIONS; ++i)
|
||||||
|
{
|
||||||
|
if (ws_conn[i].conn == conn)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "...start server %d\n", i);
|
||||||
|
mg_start_thread(ws_server_thread, (void *)(long)i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// websocket_close_handler()
|
||||||
|
// When websocket is closed, tell the associated server to shut down
|
||||||
|
static void websocket_close_handler(struct mg_connection *conn) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
//fprintf(stderr, "close handler\n"); /* called for every close, not just websockets */
|
||||||
|
|
||||||
|
for(i=0; i < CONNECTIONS; ++i)
|
||||||
|
{
|
||||||
|
if (ws_conn[i].conn == conn)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "...close server %d\n", i);
|
||||||
|
ws_conn[i].closing = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arguments:
|
||||||
|
// flags: first byte of websocket frame, see websocket RFC,
|
||||||
|
// http://tools.ietf.org/html/rfc6455, section 5.2
|
||||||
|
// data, data_len: payload data. Mask, if any, is already applied.
|
||||||
|
static int websocket_data_handler(struct mg_connection *conn, int flags,
|
||||||
|
char *data, size_t data_len) {
|
||||||
|
int i;
|
||||||
|
int wsd;
|
||||||
|
|
||||||
|
for(i=0; i < CONNECTIONS; ++i)
|
||||||
|
{
|
||||||
|
if (ws_conn[i].conn == conn)
|
||||||
|
{
|
||||||
|
wsd = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i >= CONNECTIONS)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Received websocket data from unknown connection\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & 0x80)
|
||||||
|
{
|
||||||
|
flags &= 0x7f;
|
||||||
|
switch (flags)
|
||||||
|
{
|
||||||
|
case WEBSOCKET_OPCODE_CONTINUATION:
|
||||||
|
fprintf(stderr, "CONTINUATION...\n");
|
||||||
|
break;
|
||||||
|
case WEBSOCKET_OPCODE_TEXT:
|
||||||
|
fprintf(stderr, "TEXT: %-.*s\n", (int)data_len, data);
|
||||||
|
/*** interpret data as commands here ***/
|
||||||
|
if (strncmp("update on", data, data_len)== 0)
|
||||||
|
{
|
||||||
|
/* turn on updates */
|
||||||
|
ws_conn[wsd].update = 1;
|
||||||
|
/* echo back */
|
||||||
|
mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, data, data_len);
|
||||||
|
}
|
||||||
|
else if (strncmp("update off", data, data_len)== 0)
|
||||||
|
{
|
||||||
|
/* turn off updates */
|
||||||
|
ws_conn[wsd].update = 0;
|
||||||
|
/* echo back */
|
||||||
|
mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, data, data_len);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WEBSOCKET_OPCODE_BINARY:
|
||||||
|
fprintf(stderr, "BINARY...\n");
|
||||||
|
break;
|
||||||
|
case WEBSOCKET_OPCODE_CONNECTION_CLOSE:
|
||||||
|
fprintf(stderr, "CLOSE...\n");
|
||||||
|
/* If client initiated close, respond with close message in acknowlegement */
|
||||||
|
if (!ws_conn[wsd].closing)
|
||||||
|
{
|
||||||
|
mg_websocket_write(conn, WEBSOCKET_OPCODE_CONNECTION_CLOSE, data, data_len);
|
||||||
|
ws_conn[wsd].closing = 1; /* we should not send addional messages when close requested/acknowledged */
|
||||||
|
}
|
||||||
|
return 0; /* time to close the connection */
|
||||||
|
break;
|
||||||
|
case WEBSOCKET_OPCODE_PING:
|
||||||
|
/* client sent PING, respond with PONG */
|
||||||
|
mg_websocket_write(conn, WEBSOCKET_OPCODE_PONG, data, data_len);
|
||||||
|
break;
|
||||||
|
case WEBSOCKET_OPCODE_PONG:
|
||||||
|
/* received PONG to our PING, no action */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Unknown flags: %02x\n", flags);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1; /* keep connection open */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
char server_name[40];
|
||||||
|
struct mg_context *ctx;
|
||||||
|
struct mg_callbacks callbacks;
|
||||||
|
const char *options[] = {
|
||||||
|
"listening_ports", "8080",
|
||||||
|
"document_root", "docroot",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
/* get simple greeting for the web server */
|
||||||
|
snprintf(server_name, sizeof(server_name),
|
||||||
|
"Civetweb websocket server v. %s",
|
||||||
|
mg_version());
|
||||||
|
|
||||||
|
memset(&callbacks, 0, sizeof(callbacks));
|
||||||
|
callbacks.websocket_connect = websocket_connect_handler;
|
||||||
|
callbacks.websocket_ready = websocket_ready_handler;
|
||||||
|
callbacks.websocket_data = websocket_data_handler;
|
||||||
|
callbacks.connection_close = websocket_close_handler;
|
||||||
|
|
||||||
|
ctx = mg_start(&callbacks, NULL, options);
|
||||||
|
|
||||||
|
/* show the greeting and some basic information */
|
||||||
|
printf("%s started on port(s) %s with web root [%s]\n",
|
||||||
|
server_name, mg_get_option(ctx, "listening_ports"),
|
||||||
|
mg_get_option(ctx, "document_root"));
|
||||||
|
|
||||||
|
getchar(); // Wait until user hits "enter"
|
||||||
|
mg_stop(ctx);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Reference in New Issue
Block a user