From 2f3703702987e31249d4c5c9d1f90cebf1ffa9e8 Mon Sep 17 00:00:00 2001 From: matt123p Date: Tue, 24 Sep 2019 20:42:40 +0100 Subject: [PATCH] Add function so that the total number of web-socket clients can be limited (#591) * Add function so that the total number of web-socket clients can be limited easily. This is to cope with a problem when a browser does not close the web-socket connection correctly. I have observed this on Chromium based browsers. The memory leak will eventually lead to the server crashing. Normally only one connection per client is required, so limiting the number of connections would not normally cause any problems. * Prevent an assertion failure when using WebSockets Frequently when using Web Sockets you will get the assert failure: assertion "new_rcv_ann_wnd <= 0xffff" failed: file "/Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/lwip/lwip/src/core/tcp.c", line 779, function: tcp_update_rcv_ann_wnd This will happen particulary when you close the browser window. This change prevents the issue from occuring. * Do not use thread locking with the ESP8266, but instead use an empty placeholder class that can be used to implement locking at a later date. * Do not use thread locking with the ESP8266, but instead use an empty placeholder class that can be used to implement locking at a later date. * Add function so that the total number of web-socket clients can be limited easily. This is to cope with a problem when a browser does not close the web-socket connection correctly. I have observed this on Chromium based browsers. The memory leak will eventually lead to the server crashing. Normally only one connection per client is required, so limiting the number of connections would not normally cause any problems. * Set the default number of ws clients dependent on processor. --- README.md | 11 +++++++++++ examples/ESP_AsyncFSBrowser/ESP_AsyncFSBrowser.ino | 1 + src/AsyncWebSocket.cpp | 7 +++++++ src/AsyncWebSocket.h | 7 +++++++ 4 files changed, 26 insertions(+) diff --git a/README.md b/README.md index f227c55..c1bd715 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,7 @@ To use this library you might need to have the latest git versions of [ESP32](ht - [Async WebSocket Event](#async-websocket-event) - [Methods for sending data to a socket client](#methods-for-sending-data-to-a-socket-client) - [Direct access to web socket message buffer](#direct-access-to-web-socket-message-buffer) + - [Limiting the number of web socket clients](#limiting-the-number-of-web-socket-clients) - [Async Event Source Plugin](#async-event-source-plugin) - [Setup Event Source on the server](#setup-event-source-on-the-server) - [Setup Event Source in the browser](#setup-event-source-in-the-browser) @@ -1126,6 +1127,16 @@ void sendDataWs(AsyncWebSocketClient * client) } ``` +### Limiting the number of web socket clients +Browsers sometimes do not correctly close the websocket connection, even when the close() function is called in javascript. This will eventually exhaust the web server's resources and will cause the server to crash. Periodically calling the cleanClients() function from the main loop() function limits the number of clients by closing the oldest client when the maximum number of clients has been exceeded. This can called be every cycle, however, if you wish to use less power, then calling as infrequently as once per second is sufficient. + +```cpp +void loop(){ + ws.cleanupClients(); +} +``` + + ## Async Event Source Plugin The server includes EventSource (Server-Sent Events) plugin which can be used to send short text events to the browser. Difference between EventSource and WebSockets is that EventSource is single direction, text-only protocol. diff --git a/examples/ESP_AsyncFSBrowser/ESP_AsyncFSBrowser.ino b/examples/ESP_AsyncFSBrowser/ESP_AsyncFSBrowser.ino index 73451c5..0323cc1 100644 --- a/examples/ESP_AsyncFSBrowser/ESP_AsyncFSBrowser.ino +++ b/examples/ESP_AsyncFSBrowser/ESP_AsyncFSBrowser.ino @@ -217,4 +217,5 @@ void setup(){ void loop(){ ArduinoOTA.handle(); + ws.cleanupClients(); } diff --git a/src/AsyncWebSocket.cpp b/src/AsyncWebSocket.cpp index fd1c7b8..d1b3eab 100644 --- a/src/AsyncWebSocket.cpp +++ b/src/AsyncWebSocket.cpp @@ -927,6 +927,13 @@ void AsyncWebSocket::closeAll(uint16_t code, const char * message){ } } +void AsyncWebSocket::cleanupClients(uint16_t maxClients) +{ + if (count() > maxClients){ + _clients.front()->close(); + } +} + void AsyncWebSocket::ping(uint32_t id, uint8_t *data, size_t len){ AsyncWebSocketClient * c = client(id); if(c) diff --git a/src/AsyncWebSocket.h b/src/AsyncWebSocket.h index 8e2954e..0234dd7 100644 --- a/src/AsyncWebSocket.h +++ b/src/AsyncWebSocket.h @@ -37,6 +37,12 @@ #include #endif +#ifdef ESP32 +#define DEFAULT_MAX_WS_CLIENTS 8 +#else +#define DEFAULT_MAX_WS_CLIENTS 4 +#endif + class AsyncWebSocket; class AsyncWebSocketResponse; class AsyncWebSocketClient; @@ -255,6 +261,7 @@ class AsyncWebSocket: public AsyncWebHandler { void close(uint32_t id, uint16_t code=0, const char * message=NULL); void closeAll(uint16_t code=0, const char * message=NULL); + void cleanupClients(uint16_t maxClients = DEFAULT_MAX_WS_CLIENTS); void ping(uint32_t id, uint8_t *data=NULL, size_t len=0); void pingAll(uint8_t *data=NULL, size_t len=0); // done