diff --git a/README.md b/README.md index 5041cbf..c829ef4 100644 --- a/README.md +++ b/README.md @@ -3,47 +3,47 @@ WebSocket Server and Client for Arduino a WebSocket Server and Client for Arduino based on RFC6455. - + ##### Supported features of RFC6455 ##### - text frame - binary frame - connection close - ping - pong - -##### Not supported features of RFC6455 ##### - continuation frame - + ##### Limitations ##### - max input length is limited to the ram size and the ```WEBSOCKETS_MAX_DATA_SIZE``` define - max output length has no limit (the hardware is the limit) - Client send big frames with mask 0x00000000 (on AVR all frames) - + - continuation frame reassembly need to be handled in the application code + ##### Limitations for Async ##### - Functions called from within the context of the websocket event might not honor `yield()` and/or `delay()`. See [this issue](https://github.com/Links2004/arduinoWebSockets/issues/58#issuecomment-192376395) for more info and a potential workaround. - wss / SSL is not possible. - + ##### Supported Hardware ##### - ESP8266 [Arduino for ESP8266](https://github.com/Links2004/Arduino) - ESP31B - - ATmega328 with Ethernet Shield (ATmega branch) - - ATmega328 with enc28j60 (ATmega branch) - - ATmega2560 with Ethernet Shield (ATmega branch) - - ATmega2560 with enc28j60 (ATmega branch) - + - Particle with STM32 ARM Cortex M3 + - ATmega328 with Ethernet Shield (ATmega branch) + - ATmega328 with enc28j60 (ATmega branch) + - ATmega2560 with Ethernet Shield (ATmega branch) + - ATmega2560 with enc28j60 (ATmega branch) + ###### Note: ###### version 2.0 and up is not compatible with AVR/ATmega, check ATmega branch. - + Arduino for AVR not supports std namespace of c++. - + ### wss / SSL ### supported for: - wss client on the ESP8266 - wss / SSL is not natively supported in WebSocketsServer however it is possible to achieve secure websockets by running the device behind an SSL proxy. See [Nginx](examples/Nginx/esp8266.ssl.reverse.proxy.conf) for a - sample Nginx server configuration file to enable this. - + sample Nginx server configuration file to enable this. + ### ESP Async TCP ### This libary can run in Async TCP mode on the ESP. @@ -52,9 +52,6 @@ The mode can be activated in the ```WebSockets.h``` (see WEBSOCKETS_NETWORK_TYPE [ESPAsyncTCP](https://github.com/me-no-dev/ESPAsyncTCP) libary is required. -### Support for Particle devices ### -- ESP.getFreeHeap() replaced by macro GET_FREE_HEAP, defined by the type of device (currently only for ESP and STM32-based/Particle devices). -- Use Particle's TCPClient and TCPServer classes instead of Arduino's. ### Issues ### Submit issues to: https://github.com/Links2004/arduinoWebSockets/issues diff --git a/examples/WebSocketServer/WebSocketServerFragmentation.ino b/examples/WebSocketServer/WebSocketServerFragmentation.ino new file mode 100644 index 0000000..e51f2ac --- /dev/null +++ b/examples/WebSocketServer/WebSocketServerFragmentation.ino @@ -0,0 +1,94 @@ +/* + * WebSocketServer.ino + * + * Created on: 22.05.2015 + * + */ + +#include + +#include +#include +#include +#include + +ESP8266WiFiMulti WiFiMulti; + +WebSocketsServer webSocket = WebSocketsServer(81); + +#define USE_SERIAL Serial + +String fragmentBuffer = ""; + +void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght) { + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[%u] Disconnected!\n", num); + break; + case WStype_CONNECTED: { + IPAddress ip = webSocket.remoteIP(num); + USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload); + + // send message to client + webSocket.sendTXT(num, "Connected"); + } + break; + case WStype_TEXT: + USE_SERIAL.printf("[%u] get Text: %s\n", num, payload); + break; + case WStype_BIN: + USE_SERIAL.printf("[%u] get binary lenght: %u\n", num, lenght); + hexdump(payload, lenght); + break; + + // Fragmentation / continuation opcode handling + // case WStype_FRAGMENT_BIN_START: + case WStype_FRAGMENT_TEXT_START: + fragmentBuffer = (char*)payload; + USE_SERIAL.printf("[%u] get start start of Textfragment: %s\n", num, payload); + break; + case WStype_FRAGMENT: + fragmentBuffer += (char*)payload; + USE_SERIAL.printf("[%u] get Textfragment : %s\n", num, payload); + break; + case WStype_FRAGMENT_FIN: + fragmentBuffer += (char*)payload; + USE_SERIAL.printf("[%u] get end of Textfragment: %s\n", num, payload); + USE_SERIAL.printf("[%u] full frame: %s\n", num, fragmentBuffer.c_str()); + break; + } + +} + +void setup() { + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //Serial.setDebugOutput(true); + USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + webSocket.begin(); + webSocket.onEvent(webSocketEvent); +} + +void loop() { + webSocket.loop(); +} + diff --git a/src/WebSockets.cpp b/src/WebSockets.cpp index 3c3d69d..118ebb7 100644 --- a/src/WebSockets.cpp +++ b/src/WebSockets.cpp @@ -426,14 +426,15 @@ void WebSockets::handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] text: %s\n", client->num, payload); // no break here! case WSop_binary: - messageReceived(client, header->opCode, payload, header->payloadLen); + case WSop_continuation: + messageReceived(client, header->opCode, payload, header->payloadLen, header->fin); break; case WSop_ping: // send pong back sendFrame(client, WSop_pong, payload, header->payloadLen, true); break; case WSop_pong: - DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get pong (%s)\n", client->num, payload); + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get pong (%s)\n", client->num, payload ? (const char*)payload : ""); break; case WSop_close: { uint16_t reasonCode = 1000; @@ -449,11 +450,7 @@ void WebSockets::handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t } clientDisconnect(client, 1000); } - break; - case WSop_continuation: - // continuation is not supported - clientDisconnect(client, 1003); - break; + break; default: clientDisconnect(client, 1002); break; diff --git a/src/WebSockets.h b/src/WebSockets.h index 272eeca..a3419bc 100644 --- a/src/WebSockets.h +++ b/src/WebSockets.h @@ -32,7 +32,11 @@ #include #endif +#ifdef DEBUG_ESP_PORT +#define DEBUG_WEBSOCKETS(...) DEBUG_ESP_PORT.printf( __VA_ARGS__ ) +#else //#define DEBUG_WEBSOCKETS(...) os_printf( __VA_ARGS__ ) +#endif #ifndef DEBUG_WEBSOCKETS #define DEBUG_WEBSOCKETS(...) @@ -142,7 +146,11 @@ typedef enum { WStype_DISCONNECTED, WStype_CONNECTED, WStype_TEXT, - WStype_BIN + WStype_BIN, + WStype_FRAGMENT_TEXT_START, + WStype_FRAGMENT_BIN_START, + WStype_FRAGMENT, + WStype_FRAGMENT_FIN, } WStype_t; typedef enum { @@ -227,7 +235,7 @@ class WebSockets { virtual void clientDisconnect(WSclient_t * client); virtual bool clientIsConnected(WSclient_t * client); - virtual void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length); + virtual void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin); void clientDisconnect(WSclient_t * client, uint16_t code, char * reason = NULL, size_t reasonLen = 0); bool sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload = NULL, size_t length = 0, bool mask = false, bool fin = true, bool headerToPayload = false); diff --git a/src/WebSocketsClient.cpp b/src/WebSocketsClient.cpp index 18c9f6c..76e354c 100644 --- a/src/WebSocketsClient.cpp +++ b/src/WebSocketsClient.cpp @@ -272,16 +272,19 @@ void WebSocketsClient::setAuthorization(const char * auth) { * @param payload uint8_t * * @param lenght size_t */ -void WebSocketsClient::messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t lenght) { +void WebSocketsClient::messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t lenght, bool fin) { WStype_t type = WStype_ERROR; switch(opcode) { case WSop_text: - type = WStype_TEXT; + type = fin ? WStype_TEXT : WStype_FRAGMENT_TEXT_START; break; case WSop_binary: - type = WStype_BIN; + type = fin ? WStype_BIN : WStype_FRAGMENT_BIN_START; break; + case WSop_continuation: + type = fin ? WStype_FRAGMENT_FIN : WStype_FRAGMENT; + break; } runCbEvent(type, payload, lenght); diff --git a/src/WebSocketsClient.h b/src/WebSocketsClient.h index bdbbb3b..83913dc 100644 --- a/src/WebSocketsClient.h +++ b/src/WebSocketsClient.h @@ -87,7 +87,7 @@ class WebSocketsClient: private WebSockets { WebSocketClientEvent _cbEvent; - void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length); + void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin); void clientDisconnect(WSclient_t * client); bool clientIsConnected(WSclient_t * client); diff --git a/src/WebSocketsServer.cpp b/src/WebSocketsServer.cpp index 9e6037e..a7d3dc3 100644 --- a/src/WebSocketsServer.cpp +++ b/src/WebSocketsServer.cpp @@ -470,15 +470,18 @@ bool WebSocketsServer::newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient) { * @param payload uint8_t * * @param lenght size_t */ -void WebSocketsServer::messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t lenght) { +void WebSocketsServer::messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t lenght, bool fin) { WStype_t type = WStype_ERROR; switch(opcode) { case WSop_text: - type = WStype_TEXT; + type = fin ? WStype_TEXT : WStype_FRAGMENT_TEXT_START; break; case WSop_binary: - type = WStype_BIN; + type = fin ? WStype_BIN : WStype_FRAGMENT_BIN_START; + break; + case WSop_continuation: + type = fin ? WStype_FRAGMENT_FIN : WStype_FRAGMENT; break; } diff --git a/src/WebSocketsServer.h b/src/WebSocketsServer.h index 33037ed..6185e92 100644 --- a/src/WebSocketsServer.h +++ b/src/WebSocketsServer.h @@ -113,7 +113,7 @@ protected: bool newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient); - void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length); + void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin); void clientDisconnect(WSclient_t * client); bool clientIsConnected(WSclient_t * client);