mirror of
https://github.com/Links2004/arduinoWebSockets.git
synced 2025-07-12 23:16:29 +02:00
message browser to client working
This commit is contained in:
202
src/WebSockets.cpp
Normal file
202
src/WebSockets.cpp
Normal file
@ -0,0 +1,202 @@
|
||||
/**
|
||||
* @file WebSockets.cpp
|
||||
* @date 20.05.2015
|
||||
* @author Markus Sattler
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the WebSockets for Arduino.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "WebSockets.h"
|
||||
|
||||
/**
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-------+-+-------------+-------------------------------+
|
||||
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|
||||
|I|S|S|S| (4) |A| (7) | (16/64) |
|
||||
|N|V|V|V| |S| | (if payload len==126/127) |
|
||||
| |1|2|3| |K| | |
|
||||
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|
||||
| Extended payload length continued, if payload len == 127 |
|
||||
+ - - - - - - - - - - - - - - - +-------------------------------+
|
||||
| |Masking-key, if MASK set to 1 |
|
||||
+-------------------------------+-------------------------------+
|
||||
| Masking-key (continued) | Payload Data |
|
||||
+-------------------------------- - - - - - - - - - - - - - - - +
|
||||
: Payload Data continued ... :
|
||||
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|
||||
| Payload Data continued ... |
|
||||
+---------------------------------------------------------------+
|
||||
*
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
WSop_continuation = 0x00, ///< %x0 denotes a continuation frame
|
||||
WSop_text = 0x01, ///< %x1 denotes a text frame
|
||||
WSop_binary = 0x02, ///< %x2 denotes a binary frame
|
||||
///< %x3-7 are reserved for further non-control frames
|
||||
WSop_close = 0x08, ///< %x8 denotes a connection close
|
||||
WSop_ping = 0x09, ///< %x9 denotes a ping
|
||||
WSop_pong = 0x0A ///< %xA denotes a pong
|
||||
///< %xB-F are reserved for further control frames
|
||||
} WSopcode_t;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* handle the WebSocket stream
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
*/
|
||||
void WebSockets::handleWebsocket(WSclient_t * client) {
|
||||
|
||||
uint8_t buffer[8] = { 0 };
|
||||
|
||||
bool fin;
|
||||
bool rsv1;
|
||||
bool rsv2;
|
||||
bool rsv3;
|
||||
WSopcode_t opCode;
|
||||
bool mask;
|
||||
size_t payloadLen;
|
||||
|
||||
uint8_t maskKey[4];
|
||||
|
||||
uint8_t * payload = NULL;
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleWebsocket] ------- read massage frame -------\n", client->num);
|
||||
|
||||
if(!readWait(client, buffer, 2)) {
|
||||
//timeout
|
||||
clientDisconnect(client);
|
||||
return;
|
||||
}
|
||||
|
||||
// split first 2 bytes in the data
|
||||
fin = ((buffer[0] >> 7) & 0x01);
|
||||
rsv1 = ((buffer[0] >> 6) & 0x01);
|
||||
rsv2 = ((buffer[0] >> 5) & 0x01);
|
||||
rsv3 = ((buffer[0] >> 4) & 0x01);
|
||||
opCode = (WSopcode_t)(buffer[0] & 0x0F);
|
||||
|
||||
mask = ((buffer[1] >> 7) & 0x01);
|
||||
payloadLen = (WSopcode_t)(buffer[1] & 0x7F);
|
||||
|
||||
if(payloadLen == 126) {
|
||||
if(!readWait(client, buffer, 2)) {
|
||||
//timeout
|
||||
clientDisconnect(client);
|
||||
return;
|
||||
}
|
||||
payloadLen = buffer[0] << 8 | buffer[1];
|
||||
} else if(payloadLen == 127) {
|
||||
// read 64bit inteager as Lenght
|
||||
if(!readWait(client, buffer, 8)) {
|
||||
//timeout
|
||||
clientDisconnect(client);
|
||||
return;
|
||||
}
|
||||
|
||||
if(buffer[0] != 0 || buffer[1] != 0 || buffer[2] != 0 || buffer[3] != 0) {
|
||||
// really to big!
|
||||
payloadLen = 0xFFFFFFFF;
|
||||
} else {
|
||||
payloadLen = buffer[4] << 24 | buffer[5] << 16 | buffer[6] << 8 | buffer[7];
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleWebsocket] fin: %u rsv1: %u rsv2: %u rsv3 %u opCode: %u\n", client->num, fin, rsv1, rsv2, rsv3, opCode);
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleWebsocket] mask: %u payloadLen: %u\n", client->num, mask, payloadLen);
|
||||
|
||||
if(payloadLen > WEBSOCKETS_MAX_DATA_SIZE) {
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleWebsocket] payload to big! (%u)\n", client->num, payloadLen);
|
||||
clientDisconnect(client);
|
||||
return;
|
||||
}
|
||||
|
||||
if(mask) {
|
||||
client->tcp.read(maskKey, 4);
|
||||
}
|
||||
|
||||
if(payloadLen > 0) {
|
||||
// if text data we need one more
|
||||
payload = (uint8_t *) malloc(payloadLen+1);
|
||||
|
||||
if(!payload) {
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleWebsocket] to less memory to handle payload %d!\n", client->num, payloadLen);
|
||||
clientDisconnect(client);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!readWait(client, payload, payloadLen)) {
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleWebsocket] missing data!\n", client->num);
|
||||
clientDisconnect(client);
|
||||
return;
|
||||
}
|
||||
|
||||
if(mask) {
|
||||
//decode XOR
|
||||
for (size_t i = 0; i < payloadLen; i++) {
|
||||
payload[i] = (payload[i] ^ maskKey[i % 4]);
|
||||
}
|
||||
}
|
||||
|
||||
if(opCode == WSop_text) {
|
||||
payload[payloadLen] = 0x00;
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleWebsocket] Text: %s\n", client->num, payload);
|
||||
} else {
|
||||
clientDisconnect(client);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool WebSockets::readWait(WSclient_t * client, uint8_t *out, size_t n) {
|
||||
unsigned long t = millis();
|
||||
size_t len;
|
||||
|
||||
while(n > 0) {
|
||||
if(!client->tcp.connected()) {
|
||||
DEBUG_WEBSOCKETS("[readWait] Receive not connected - 1!\n");
|
||||
return false;
|
||||
}
|
||||
while(!client->tcp.available()) {
|
||||
if(!client->tcp.connected()) {
|
||||
DEBUG_WEBSOCKETS("[readWait] Receive not connected - 2!\n");
|
||||
return false;
|
||||
}
|
||||
if((millis() - t) > WEBSOCKETS_TCP_TIMEOUT) {
|
||||
DEBUG_WEBSOCKETS("[readWait] Receive TIMEOUT!\n");
|
||||
return false;
|
||||
}
|
||||
delay(0);
|
||||
}
|
||||
|
||||
len = client->tcp.read((uint8_t*) out, n);
|
||||
if(len) {
|
||||
t = millis();
|
||||
out += len;
|
||||
n -= len;
|
||||
//DEBUG_WEBSOCKETS("Receive %d left %d!\n", len, n);
|
||||
} else {
|
||||
//DEBUG_WEBSOCKETS("Receive %d left %d!\n", len, n);
|
||||
}
|
||||
delay(0);
|
||||
}
|
||||
return true;
|
||||
}
|
@ -27,13 +27,67 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifdef ESP8266
|
||||
#include <ESP8266WiFi.h>
|
||||
#else
|
||||
#include <UIPEthernet.h>
|
||||
#ifndef UIPETHERNET_H
|
||||
#include <Ethernet.h>
|
||||
#include <SPI.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define DEBUG_WEBSOCKETS(...) Serial1.printf( __VA_ARGS__ ); Serial1.flush()
|
||||
|
||||
#ifndef DEBUG_WEBSOCKETS
|
||||
#define DEBUG_WEBSOCKETS(...)
|
||||
#endif
|
||||
|
||||
#define WEBSOCKETS_MAX_DATA_SIZE (15*1024)
|
||||
#define WEBSOCKETS_TCP_TIMEOUT (1000)
|
||||
|
||||
typedef enum {
|
||||
WSC_NOT_CONNECTED,
|
||||
WSC_HEADER,
|
||||
WSC_CONNECTED
|
||||
} WSclientsStatus_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t num; ///< connection number
|
||||
|
||||
WSclientsStatus_t status;
|
||||
#ifdef ESP8266
|
||||
WiFiClient tcp;
|
||||
#else
|
||||
#ifdef UIPETHERNET_H
|
||||
UIPClient tcp;
|
||||
#else
|
||||
EthernetClient tcp;
|
||||
#endif
|
||||
#endif
|
||||
String cUrl; ///< http url
|
||||
|
||||
bool cIsUpgrade; ///< Connection == Upgrade
|
||||
bool cIsWebsocket; ///< Upgrade == websocket
|
||||
|
||||
String cKey; ///< client Sec-WebSocket-Key
|
||||
String cProtocol; ///< client Sec-WebSocket-Protocol
|
||||
String cExtensions; ///< client Sec-WebSocket-Extensions
|
||||
int cVersion; ///< client Sec-WebSocket-Version
|
||||
|
||||
String sKey; ///< server Sec-WebSocket-Key
|
||||
|
||||
} WSclient_t;
|
||||
|
||||
class WebSockets {
|
||||
protected:
|
||||
|
||||
void handleWebsocket(WSclient_t * client);
|
||||
|
||||
virtual void clientDisconnect(WSclient_t * client);
|
||||
virtual bool clientIsConnected(WSclient_t * client);
|
||||
|
||||
bool readWait(WSclient_t * client, uint8_t *out, size_t n);
|
||||
};
|
||||
|
||||
#endif /* WEBSOCKETS_H_ */
|
||||
|
@ -35,12 +35,13 @@ WebSocketsServer::WebSocketsServer(uint16_t port) {
|
||||
_port = port;
|
||||
_server = new WiFiServer(port);
|
||||
}
|
||||
|
||||
WebSocketsServer::~WebSocketsServer() {
|
||||
// todo how to close server?
|
||||
}
|
||||
|
||||
void WebSocketsServer::begin(void) {
|
||||
WSclients_t * client;
|
||||
WSclient_t * client;
|
||||
|
||||
// init client storage
|
||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
||||
@ -76,9 +77,9 @@ void WebSocketsServer::loop(void) {
|
||||
|
||||
/**
|
||||
* Disconnect an client
|
||||
* @param num uint8_t index of _clients array
|
||||
* @param client WSclients_t * ptr to the client struct
|
||||
*/
|
||||
void WebSocketsServer::clientDisconnect(WSclients_t * client) {
|
||||
void WebSocketsServer::clientDisconnect(WSclient_t * client) {
|
||||
|
||||
if(client->tcp) {
|
||||
client->tcp.stop();
|
||||
@ -101,10 +102,10 @@ void WebSocketsServer::clientDisconnect(WSclients_t * client) {
|
||||
|
||||
/**
|
||||
* get client state
|
||||
* @param num uint8_t index of _clients array
|
||||
* @param client WSclients_t * ptr to the client struct
|
||||
* @return true = conneted
|
||||
*/
|
||||
bool WebSocketsServer::clientIsConnected(WSclients_t * client) {
|
||||
bool WebSocketsServer::clientIsConnected(WSclient_t * client) {
|
||||
|
||||
if(client->status != WSC_NOT_CONNECTED && client->tcp.connected()) {
|
||||
return true;
|
||||
@ -121,7 +122,7 @@ bool WebSocketsServer::clientIsConnected(WSclients_t * client) {
|
||||
* Handle incomming Connection Request
|
||||
*/
|
||||
void WebSocketsServer::handleNewClients(void) {
|
||||
WSclients_t * client;
|
||||
WSclient_t * client;
|
||||
while(_server->hasClient()) {
|
||||
bool ok = false;
|
||||
// search free list entry for client
|
||||
@ -135,7 +136,7 @@ void WebSocketsServer::handleNewClients(void) {
|
||||
client->tcp = _server->available();
|
||||
client->tcp.setNoDelay(true);
|
||||
// set Timeout for readBytesUntil and readStringUntil
|
||||
client->tcp.setTimeout(1000);
|
||||
client->tcp.setTimeout(WEBSOCKETS_TCP_TIMEOUT);
|
||||
client->status = WSC_HEADER;
|
||||
|
||||
IPAddress ip = client->tcp.remoteIP();
|
||||
@ -162,7 +163,7 @@ void WebSocketsServer::handleNewClients(void) {
|
||||
*/
|
||||
void WebSocketsServer::handleClientData(void) {
|
||||
|
||||
WSclients_t * client;
|
||||
WSclient_t * client;
|
||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
||||
client = &_clients[i];
|
||||
if(clientIsConnected(client)) {
|
||||
@ -174,6 +175,7 @@ void WebSocketsServer::handleClientData(void) {
|
||||
handleHeader(client);
|
||||
break;
|
||||
case WSC_CONNECTED:
|
||||
WebSockets::handleWebsocket(client);
|
||||
break;
|
||||
default:
|
||||
clientDisconnect(client);
|
||||
@ -185,30 +187,11 @@ void WebSocketsServer::handleClientData(void) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
[WS-Server][0] new client from 192.168.2.23
|
||||
[WS-Server][0][handleHeader] RX: GET /test HTTP/1.1
|
||||
[WS-Server][0][handleHeader] RX: Host: 10.11.2.1:81
|
||||
[WS-Server][0][handleHeader] RX: Connection: Upgrade
|
||||
[WS-Server][0][handleHeader] RX: Pragma: no-cache
|
||||
[WS-Server][0][handleHeader] RX: Cache-Control: no-cache
|
||||
[WS-Server][0][handleHeader] RX: Upgrade: websocket
|
||||
[WS-Server][0][handleHeader] RX: Origin: null
|
||||
[WS-Server][0][handleHeader] RX: Sec-WebSocket-Version: 13
|
||||
[WS-Server][0][handleHeader] RX: DNT: 1
|
||||
[WS-Server][0][handleHeader] RX: User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.65 Safari/537.36
|
||||
[WS-Server][0][handleHeader] RX: Accept-Encoding: gzip, deflate, sdch
|
||||
[WS-Server][0][handleHeader] RX: Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4
|
||||
[WS-Server][0][handleHeader] RX: Sec-WebSocket-Key: FRddfIKSePnzAzKOqUGI/Q==
|
||||
[WS-Server][0][handleHeader] RX: Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
|
||||
[WS-Server][0][handleHeader] RX: Sec-WebSocket-Protocol: esp8266, test
|
||||
*/
|
||||
|
||||
/**
|
||||
* handle the WebSocket headder reading
|
||||
* @param num uint8_t index of _clients array
|
||||
* handle the WebSocket header reading
|
||||
* @param client WSclients_t * ptr to the client struct
|
||||
*/
|
||||
void WebSocketsServer::handleHeader(WSclients_t * client) {
|
||||
void WebSocketsServer::handleHeader(WSclient_t * client) {
|
||||
|
||||
String headerLine = client->tcp.readStringUntil('\n');
|
||||
headerLine.trim(); // remove \r
|
||||
@ -268,7 +251,6 @@ void WebSocketsServer::handleHeader(WSclients_t * client) {
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] Websocket connection incomming.\n", client->num);
|
||||
|
||||
|
||||
// generate Sec-WebSocket-Accept key
|
||||
uint8_t sha1HashBin[20] = {0};
|
||||
sha1(client->cKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", &sha1HashBin[0]);
|
||||
@ -295,11 +277,15 @@ void WebSocketsServer::handleHeader(WSclients_t * client) {
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"Sec-WebSocket-Accept: ");
|
||||
client->tcp.write(client->sKey.c_str(), client->sKey.length());
|
||||
client->tcp.write("\r\n"
|
||||
"Sec-WebSocket-Protocol: ");
|
||||
client->tcp.write(client->cProtocol.c_str(), client->cProtocol.length()); // support any protocol for now
|
||||
client->tcp.write("\r\n"
|
||||
"\r\n");
|
||||
client->tcp.write("\r\n");
|
||||
|
||||
if(client->cProtocol.length() > 0) {
|
||||
// todo add api to set Protocol of Server
|
||||
client->tcp.write("Sec-WebSocket-Protocol: esp8266\r\n");
|
||||
}
|
||||
|
||||
// header end
|
||||
client->tcp.write("\r\n");
|
||||
|
||||
} else {
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] no Websocket connection close.\n", client->num);
|
||||
@ -315,3 +301,6 @@ void WebSocketsServer::handleHeader(WSclients_t * client) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -37,44 +37,12 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#include "WebSockets.h"
|
||||
|
||||
#define WEBSOCKETS_SERVER_CLIENT_MAX (5)
|
||||
|
||||
typedef enum {
|
||||
WSC_NOT_CONNECTED,
|
||||
WSC_HEADER,
|
||||
WSC_CONNECTED
|
||||
} WSclientsStatus_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t num; ///< connection number
|
||||
|
||||
WSclientsStatus_t status;
|
||||
#ifdef ESP8266
|
||||
WiFiClient tcp;
|
||||
#else
|
||||
#ifdef UIPETHERNET_H
|
||||
UIPClient tcp;
|
||||
#else
|
||||
EthernetClient tcp;
|
||||
#endif
|
||||
#endif
|
||||
String cUrl; ///< http url
|
||||
|
||||
bool cIsUpgrade; ///< Connection == Upgrade
|
||||
bool cIsWebsocket; ///< Upgrade == websocket
|
||||
|
||||
String cKey; ///< client Sec-WebSocket-Key
|
||||
String cProtocol; ///< client Sec-WebSocket-Protocol
|
||||
String cExtensions; ///< client Sec-WebSocket-Extensions
|
||||
int cVersion; ///< client Sec-WebSocket-Version
|
||||
|
||||
String sKey; ///< server Sec-WebSocket-Key
|
||||
|
||||
} WSclients_t;
|
||||
|
||||
class WebSocketsServer {
|
||||
class WebSocketsServer: private WebSockets {
|
||||
public:
|
||||
WebSocketsServer(uint16_t port);
|
||||
~WebSocketsServer(void);
|
||||
@ -95,17 +63,17 @@ private:
|
||||
#endif
|
||||
#endif
|
||||
|
||||
WSclients_t _clients[WEBSOCKETS_SERVER_CLIENT_MAX];
|
||||
WSclient_t _clients[WEBSOCKETS_SERVER_CLIENT_MAX];
|
||||
|
||||
|
||||
void clientDisconnect(WSclients_t * client);
|
||||
bool clientIsConnected(WSclients_t * client);
|
||||
void clientDisconnect(WSclient_t * client);
|
||||
bool clientIsConnected(WSclient_t * client);
|
||||
|
||||
void handleNewClients(void);
|
||||
void handleClientData(void);
|
||||
|
||||
void handleHeader(WSclients_t * client);
|
||||
|
||||
void handleHeader(WSclient_t * client);
|
||||
void handleWebsocket(WSclient_t * client);
|
||||
|
||||
};
|
||||
|
||||
|
@ -2,26 +2,21 @@
|
||||
<head>
|
||||
|
||||
<script>
|
||||
var connection = new WebSocket('ws://10.11.2.1:81/test', ['esp8266']);
|
||||
|
||||
|
||||
var connection = new WebSocket('ws://10.11.2.1:81/test', ['esp8266', 'test']);
|
||||
|
||||
// When the connection is open, send some data to the server
|
||||
connection.onopen = function () {
|
||||
connection.send('ping'); // Send the message 'Ping' to the server
|
||||
connection.send('Message from Browser to ESP8266 yay its Working!!');
|
||||
connection.send('ping');
|
||||
};
|
||||
|
||||
// Log errors
|
||||
connection.onerror = function (error) {
|
||||
console.log('WebSocket Error ' + error);
|
||||
console.log('WebSocket Error ', error);
|
||||
};
|
||||
|
||||
// Log messages from the server
|
||||
connection.onmessage = function (e) {
|
||||
console.log('Server: ' + e.data);
|
||||
console.log('Server: ', e.data);
|
||||
};
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
Reference in New Issue
Block a user