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()
так что вполне могут быть проблемы вне этих функций.