AsyncTCP на ESP32 и проблемы с нечетной кучей / сокетом с SOFTAP

Я борюсь с проблемой, когда ESP32 работает как AP с AsyncTCP, соединяющим несколько клиентов ESP32. AP получает некоторые данные JSON и отвечает некоторыми данными JSON. Без функции handleData() код работает на 100% без проблем. Куча статична, когда клиенты не подключаются, и проблемы возникают только тогда, когда клиенты начинают подключаться.

Может ли кто-нибудь увидеть в моем коде что-нибудь, что могло бы вызвать повреждение кучи или другие странности памяти?

static void handleData(void* arg, AsyncClient* client, void *data, size_t len) {
int i = 0, j = 0;
char clientData[CLIENT_DATA_MAX];
char packetData[len];
char *packetBuf;

   packetBuf = (char *)data;
   clientData[0] = '\0';

   for (i=0;i <= len;i++) {
      packetData[j] = packetBuf[i]; //packetBuf[i];

      if ((packetData[j] == '\n') || (i == len)) {
         packetData[j] = '\0';
         if ((j > 0) && (packetData[0] != '\n') && (packetData[0] != '\r')) {
            // See sensorData() below...
            parseData.function(packetData, clientData);
            if (clientData != NULL) {
               // TCP reply to client
               if (client->space() > 32 && client->canSend()) {
                 client->write(clientData);
               }
            }
         }
         j = 0;
      } else
         j++;
   }
}

void sensorData(void *data, void *retData) {
StaticJsonDocument<CLIENT_DATA_MAX> fields;
StaticJsonDocument<CLIENT_DATA_MAX> output;
char sensor[15] = "\0";
char MAC[18] = "\0";
char value[20] = "\0";
bool sendOK = false;

   memcpy((char *)retData, "\0", 1);
   DeserializationError error = deserializeJson(fields, (char *)data, CLIENT_DATA_MAX);
   if (error) {
      DEBUG_PRINTLN(F("deserializeJson() failed"));
      return;
   }

   if (fields["type"])
      strcpy(sensor, fields["type"]);

   switch (sensor[0]) {
      case 'C': 
         if (fields["value"])
            strcpy(value, fields["value"]);
         sendOK = true;
         break;
      case 'T': //DEBUG_PRINT(F("Temp "));
         setExtTempSensor(fields["value"]);
         sendOK = true;
         break;
      case 'N': 
         output["IT"] = intTempC; //Internal temp
         output["B1"] = battLevels[0];
         serializeJson(output, (char *)retData, CLIENT_DATA_MAX-1);
         break;
   } 
   if (sendOK) {
      output["Resp"] = "Ok";
      serializeJson(output, (char *)retData, CLIENT_DATA_MAX-1);
   }
   strcat((char *)retData, "\n");
}

static void handleNewClient(void* arg, AsyncClient* client) {
   client->setRxTimeout(1000);
   client->setAckTimeout(500);
   client->onData(&handleData, NULL);
   client->onError(&handleError, NULL);
   client->onDisconnect(&handleDisconnect, NULL);
   client->onTimeout(&handleTimeOut, NULL);
}

void startServer() {
  server = new AsyncServer(WIFI_SERVER_PORT);
  server->onClient(&handleNewClient, &server)
}


2 ответа

При использовании AsyncTCP на ESP32 возникло несколько проблем. Проблемы с кучей, проблемы с сокетами, проблемы с утверждениями, тайм-ауты ACK, тайм-ауты подключения и т. Д. Переход на AsyncUDP с использованием того же кода, который показан выше, с изменениями romkey, решил все мои проблемы. (Простое использование исправлений romkey не устраняет ошибок, которые у меня были с AsyncTCP.) Я не верю, что проблема связана с AsyncTCP, а с библиотеками ESP32.

Либо вы должны объявить packetData иметь длину len + 1 или ваш for цикл должен повторяться до тех пор, пока i < len. Поскольку индекс начинается с 0,packetData[len] на самом деле байт len + 1, поэтому вы перезапишете что-то случайное, когда сохраните что-то в packetData[len] если массив только len chars long. что что-то случайное может быть указателем, хранящимся в packetBuf, что может легко вызвать повреждение кучи.

Вы всегда должны использовать strncpy() и никогда strcpy(). Аналогичным образом используйтеstrncat() скорее, чем strcat(). Не полагайтесь на правильность вычислений или на то, что размеры не меняются по мере развития вашего кода.strncpy() а также strncat()защитит от переполнения. Вам нужно будет передать длину вsensorData() сделать это, но sensorData() не следует делать предположений о доступной длине retData.

Ваш тест

    if (clientData != NULL) {

никогда не подведет, потому что clientDataявляется адресом массива и не может быть изменен. Я не уверен, что вы пытаетесь здесь проверить, но этоif всегда будет получаться.

Вы можете просто написать:

char sensor[15] = "";

вам не нужно явно назначать строку с нулевым байтом в ней.

А также

memcpy((char *)retData, "\0", 1);

эквивалентно

((char *)retData)[0] = '\0';

Какой смысл объявлять retData быть void * в аргументах sensorData()? Ваш код начинается с того, чтоchar* перед звонком sensorData() и использует его как char* внутри sensorData(). void *предназначен для обхода указателей, не беспокоясь об их типе. Здесь вам это не нужно, и в конечном итоге потребуется дополнительное приведение обратно вchar*из-за этого. Просто объявите аргументchar* и не беспокойтесь о его повторном использовании.

Вы не поделились кодом, который вызывает handleData() так что вполне могут быть проблемы вне этих функций.

Другие вопросы по тегам