Skip to content

Commit f520559

Browse files
Merge pull request #434 from ESP32Async/issue/433
Set CloseClientOnQueueFull to false by default
2 parents a9fedfe + f808e44 commit f520559

5 files changed

Lines changed: 57 additions & 17 deletions

File tree

docs/websockets.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,38 @@ const uint8_t flash_binary[] PROGMEM = { 0x01, 0x02, 0x03, 0x04 };
171171
client->binary(flash_binary, 4);
172172
```
173173

174+
### Queue full behavior: `setCloseClientOnQueueFull()`
175+
176+
When a client cannot keep up, outgoing WebSocket messages are queued.
177+
If the queue reaches `WS_MAX_QUEUED_MESSAGES`, new messages are either discarded or the client is closed, depending on this setting.
178+
179+
```cpp
180+
client->setCloseClientOnQueueFull(bool close);
181+
```
182+
183+
- `close == false` (default in this library): discard new messages when the queue is full.
184+
- `close == true`: close the client when the queue is full.
185+
186+
We recommend using `false`.
187+
188+
When the queue starts filling, prefer reducing your sending rate and/or explicitly closing the client according to your application policy.
189+
190+
We do not recommend using `true` because it can lead to a crash under certain circumstances.
191+
192+
You can combine this with:
193+
194+
- `client->queueIsFull()`
195+
- `client->queueLen()`
196+
- `ws.availableForWrite(clientId)` and `ws.availableForWriteAll()`
197+
198+
Typical usage is to set the policy when the client connects:
199+
200+
```cpp
201+
if (type == WS_EVT_CONNECT) {
202+
client->setCloseClientOnQueueFull(false); // default behavior
203+
}
204+
```
205+
174206
### Direct access to web socket message buffer
175207

176208
When sending a web socket message using the above methods a buffer is created. Under certain circumstances you might want to manipulate or populate this buffer directly from your application, for example to prevent unnecessary duplications of the data. This example below shows how to create a buffer and print data to it from an ArduinoJson object then send it.

examples/arduino/WebSocket/WebSocket.ino

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ void setup() {
9393
if (type == WS_EVT_CONNECT) {
9494
ws.textAll("new client connected");
9595
Serial.println("ws connect");
96-
client->setCloseClientOnQueueFull(false);
9796
client->ping();
9897

9998
} else if (type == WS_EVT_DISCONNECT) {

examples/idf_component/websocket/main/main.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ void setup() {
3737
if (type == WS_EVT_CONNECT) {
3838
ws.textAll("new client connected");
3939
Serial.println("ws connect");
40-
client->setCloseClientOnQueueFull(false);
4140
client->ping();
4241

4342
} else if (type == WS_EVT_DISCONNECT) {

src/AsyncWebSocket.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ bool AsyncWebSocketClient::_queueMessage(AsyncWebSocketSharedBuffer buffer, uint
467467
}
468468

469469
if (_messageQueue.size() >= WS_MAX_QUEUED_MESSAGES) {
470-
if (closeWhenFull) {
470+
if (_closeWhenFull) {
471471
_status = WS_DISCONNECTED;
472472

473473
async_ws_log_w("[%s][%" PRIu32 "] Too many messages queued: closing connection", _server->url(), _clientId);

src/AsyncWebSocket.h

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ class AsyncWebSocketClient {
225225
mutable asyncsrv::mutex_type _queue_lock;
226226
std::deque<AsyncWebSocketControl> _controlQueue;
227227
std::deque<AsyncWebSocketMessage> _messageQueue;
228-
bool closeWhenFull = true;
228+
bool _closeWhenFull = false;
229229

230230
AwsFrameInfo _pinfo;
231231

@@ -274,29 +274,39 @@ class AsyncWebSocketClient {
274274
return _pinfo;
275275
}
276276

277-
// - If "true" (default), the connection will be closed if the message queue is full.
277+
// CloseClientOnQueueFull:
278+
//
279+
// - If "true", the client will be closed if the message queue becomes full.
278280
// This is the default behavior in yubox-node-org, which is not silently discarding messages but instead closes the connection.
279281
// The big issue with this behavior is that is can cause the UI to automatically re-create a new WS connection, which can be filled again,
280282
// and so on, causing a resource exhaustion.
283+
// Also this can lead to a crash as explained in this issue: https://github.com/ESP32Async/ESPAsyncWebServer/issues/433
281284
//
282-
// - If "false", the incoming message will be discarded if the queue is full.
285+
// - If "false" (default in this library), the incoming message will be discarded if the queue is full.
283286
// This is the default behavior in the original ESPAsyncWebServer library from me-no-dev.
284287
// This behavior allows the best performance at the expense of unreliable message delivery in case the queue is full (some messages may be lost).
285288
//
286-
// - In any case, when the queue is full, a message is logged.
287-
// - IT is recommended to use the methods queueIsFull(), availableForWriteAll(), availableForWrite(clientId) to check if the queue is full before sending a message.
289+
// With recent refactorings of the library, the queue is barely used and the library supports a fast sending rate of messages. So if the queue is growing:
290+
// - either the server is sending messages at an insane fast rate, faster than what the client can acknowledge, which can be the case if the client is slow or if the messages are big and the network is slow,
291+
// - or there is a network issue causing the client to not receive messages, or network is broken. In that case, if the network is broken, the queue will fill temporarily until the connection is closed and client removed.
292+
//
293+
// In case your application requires a fast and high frequency message sending and you foresee some queue usage, you can:
294+
// - increase the queue side to allow some room
295+
// - check some functions status before or when sending in order to decrease your sending rate to let the queue drain, or take action by closing this client if necessary.
288296
//
289-
// Usage:
290-
// - can be set in the onEvent listener when connecting (event type is: WS_EVT_CONNECT)
297+
// This has to be an application-specific deicison that the library cannot take for you.
298+
// Here are a list of some functions that you can use and check the boolean value returned:
299+
// - the send methods
300+
// - queueIsFull()
301+
// - availableForWriteAll()
302+
// - availableForWrite(clientId)
291303
//
292-
// Use cases:,
293-
// - if using websocket to send logging messages, maybe some loss is acceptable.
294-
// - But if using websocket to send UI update messages, maybe the connection should be closed and the UI redrawn.
304+
// When the queue is full, a message is logged in case it is discarded.
295305
void setCloseClientOnQueueFull(bool close) {
296-
closeWhenFull = close;
306+
_closeWhenFull = close;
297307
}
298308
bool willCloseClientOnQueueFull() const {
299-
return closeWhenFull;
309+
return _closeWhenFull;
300310
}
301311

302312
IPAddress remoteIP() const;
@@ -319,8 +329,8 @@ class AsyncWebSocketClient {
319329
}
320330

321331
// data packets
322-
void message(AsyncWebSocketSharedBuffer buffer, uint8_t opcode = WS_TEXT, bool mask = false) {
323-
_queueMessage(buffer, opcode, mask);
332+
bool message(AsyncWebSocketSharedBuffer buffer, uint8_t opcode = WS_TEXT, bool mask = false) {
333+
return _queueMessage(buffer, opcode, mask);
324334
}
325335
bool queueIsFull() const;
326336
size_t queueLen() const;

0 commit comments

Comments
 (0)