me-no-dev / ESPAsyncWebServer

Async Web Server for ESP8266 and ESP32
3.58k stars 1.17k forks source link

Socket _messageQueue.length() #1386

Open LaCocoRoco opened 3 months ago

LaCocoRoco commented 3 months ago

With socket it is possible to send messages in a very high frequency. Consequently the message queue gets exhausted very fast. General this is no problem when using availableForWrite.

bool clientConnected() {
  bool clientStationConnected = WiFi.status() != WL_CONNECTED;
  bool clientAccessPointConnected = WiFi.softAPgetStationNum() < 1;
  return clientStationConnected || clientAccessPointConnected;
}

void socketTransmit(const int id, const String data) {
  if (clientConnected()) {
    if (id && webSocket.availableForWrite(id)) {
      webSocket.text(id, data);
    } else if (webSocket.availableForWriteAll()) {
      webSocket.textAll(data);
    }
  }
}

But this can get problematic when the queue is full, because messages can get lost. So i added messagesInQueue functions to AsyncWebSocket and AsyncWebSocketClient.

int AsyncWebSocket::messagesInQueue() {
  for (const auto &c : _clients) {
    return c->messagesInQueue();
  }
  return 0;
}

int AsyncWebSocketClient::messagesInQueue() {
  return _messageQueue.length();
}

With this as example half of the buffer can be used for important messages or the fastest available interval based on queue exhaustion could also be calculated.

// 020 ms intervall
void taskA() {
  // unimportend message
    if (webSocket.messagesInQueue() < WS_MAX_QUEUED_MESSAGES / 2) {
      socketTransmit(0, "unimportant data");
    }
}

// 500 ms intervall
void taskB() {
  // importend message
  socketTransmit(0, "important data");
}

Yes, there are also other solutions. I could wait until the queue is full and then reduce the interval. Or testing the overall best setting. On the other hand in some instances the queue got overloaded with big data junks. Consequently the system crashed because of memory issues.

Question: Is it possible to make the _messageQueue.length() public?

LaCocoRoco commented 3 months ago

For optimization purposes, I compare the necessary data before transmitting via socket and also combine all data into one bulk transfer. With this, it is possible to reduce the transfer speed to 10 ms, but this heavily depends on the fluctuation of the data. To test the workload of the socket, I simply print the queue length to the console. In conclusion, simply checking if the socket "queueIsNotBusy" or if the "queueIsEmpty" would be enough. On the other hand, knowing the active queue would bring more flexibility.

int AsyncWebSocket::queueIsEmpty() {
  for (const auto &c : _clients) {
    return c->queueIsEmpty();
  }
  return 0;
}

int AsyncWebSocketClient::queueIsEmpty() {
    if((_messageQueue.length() == 0) || (_status != WS_CONNECTED) ) return true;
    return false;
}