• Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

Ограничение на размер посылки в сокет-сервер

unreg

Member
Создаю простой телнет сервер. Задача сервера - получить данные от одного клиента и передать другому по wifi. Все работает, если посылка небольшая. Устроил стресс-тест и закинул в сокет пару килобайт - еспшка отрубила вайфай да и вообще тупанула. 2Kb пришло другому клиенту не полностью. Подозрение на нехватку памяти, но как новичок в программировании esp не знаю как и где это увидеть? Или какое-то ограничение на размер пакета? Подскажите, дайте ссылку. Спасибо! И вот мой скетч (не оптимизированый, так что сорян):
Код:
#include <ESP8266WiFi.h>
#include <WiFiClient.h>

byte mac[] = {
    0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
IPAddress local_IP(192, 168, 4, 22);
IPAddress gateway(192, 168, 4, 9);
IPAddress subnet(255, 255, 255, 0);

#define MAX_SRV_CLIENTS 5

WiFiServer server(23);
WiFiClient serverClients[MAX_SRV_CLIENTS];


void setup()
{
   
   
    Serial.begin(115200);
   
    WiFi.mode(WIFI_AP);
    Serial.print("Setting soft-AP configuration ... ");
    Serial.println(WiFi.softAPConfig(local_IP, gateway, subnet) ? "Ready" : "Failed!");

    Serial.print("Setting soft-AP ... ");
    Serial.println(WiFi.softAP("ZAZ", "123456789") ? "Ready" : "Failed!");

    Serial.print("Soft-AP IP address = ");
    Serial.println(WiFi.softAPIP());

    server.begin();
    server.setNoDelay(true);

}

void loop()
{

    uint8_t i;

    if (server.hasClient()) {
        for (i = 0; i < MAX_SRV_CLIENTS; i++) {
            if (!serverClients[i] || !serverClients[i].connected()) {
                if (serverClients[i]) serverClients[i].stop();
                serverClients[i] = server.available();
                Serial1.print("New client: "); Serial1.print(i);
                continue;
            }
        }

        WiFiClient serverClient = server.available();
        serverClient.stop();
    }
 
    for (i = 0; i < MAX_SRV_CLIENTS; i++) {
        if (serverClients[i] && serverClients[i].connected()) {
            if (serverClients[i].available()) {
                while (serverClients[i].available())
                {
                    size_t len = serverClients[i].available(); //смотрим кол-во данных, создаем буфер
                    uint8_t sbuf[len];
                    serverClients[i].readBytes(sbuf, len);

                    uint8_t sbuf2[len + 3];// еще буфер - добавляем номер клиента, чтоб на чат было похоже
                    sprintf((char *)sbuf2, "%i: %s", i, sbuf);

                     for (int j = 0; j < MAX_SRV_CLIENTS; j++) {//рассылаем всем посылку
                        if (serverClients[j] && serverClients[j].connected()) {
                           
                            serverClients[j].write(sbuf2, len+3);
                            delay(1);
                        }
                    }
                }
            }
        }
    }
}
 

NeoroN

Member
Потому что не надо "есть слона целиком". Данные должны обрабатываться пачками по 512 байт и если размер доступных данных меньше размера пачки - то корректировать размер пачки на меньшей. А у вас получается что еще старые не обработал а вы ему новых напихали. Проблема в строке 63 - у вас никак не проверяется чего там и в каком количестве.
 

unreg

Member
Потому что не надо "есть слона целиком". .
Как это не проверяется... ведь available как раза дает количество байтов доступных в сокете.... Старые я обработал readBytes, по аналогии с простым read, который по докам читаем каждый раз следующий байт. Собственно на этом и примеров куча. Значит ограничение 512? Тыкните в доки, не нашел честное слово. На счет коррекции пачки на меньший - это вообще невозможно, имхо. Читаем до "\0" а уж сколько там? кому ведомо?
Да, и я может не правильно понял, или вы не правильно. у меня как раз корекция и идет по available. раз ер буфера меняется от доступных данных
Блин. второй раз редактирую))) как раз там (63) и проверяется количество доступных данных емае
 
Последнее редактирование:

unreg

Member
Добавлю еще. отправлял в сокет посылку 768 Б - все отлично, потом увеличил X 3. и есп сдохла. Так что на счет 512 я сильно сомневаюсь, думаю есть какой-то лимит в самой библе ESP8266WiFi. Есть всего слона не обязательно, надо откусывать сколько дают, это всегда было аксиомой =)
 

Алексей.

Active member
Добавлю еще. отправлял в сокет посылку 768 Б - все отлично, потом увеличил X 3. и есп сдохла. Так что на счет 512 я сильно сомневаюсь, думаю есть какой-то лимит в самой библе ESP8266WiFi. Есть всего слона не обязательно, надо откусывать сколько дают, это всегда было аксиомой =)
Ничего удивительного, когда Вы выполняете sprintf((char *)sbuf2, "%i: %s", i, sbuf); Ваш первый sbuf заполнен данными не имеющими в конце завершающего нуля (за sbuf может быть мусор) и вызов sprintf сохраняет в sbuf2 строку существенно большую (неопределенно большую) чем выделено len+3 и убиваете стек.
 

Алексей.

Active member
Что касается wifi то в даташите сказано что поддерживают 802.11 b/g/n/e/i и агрегацию A-MPDU A-MSDU
максимальный пайлоад MSDU для 802.11n - 2304 байта, а MPDU и того больше - до 4k
Получается врут что ли?
 

unreg

Member
Рандомная ошибка. убрал sprintf, шлю только sbuf. закидывал пакет и по 5Кб - все прожевывалось за несколько итераций. а бывало что не прожевывалось. %30 вылетов. Как-то грустно становится, не знаю на что и думать
 

pvvx

Активный участник сообщества
Что касается wifi то в даташите сказано что поддерживают 802.11 b/g/n/e/i и агрегацию A-MPDU A-MSDU
максимальный пайлоад MSDU для 802.11n - 2304 байта, а MPDU и того больше - до 4k
Получается врут что ли?
Это не имеет никакой связи с размером буфера передачи. Обычно там в глубине LwIP, у него буфер TCP_SND_BUF в минимум 2*TCP_MSS, где TCP_MSS = 1460 байт. Указывается в lwipopts.h.
Но передача байт идет обычно в socket, а у него буфер = сколько есть Heap в RAM. Как реализована передача в недо-serverClients.write() в Arduino IDE ESP я не знаю и знать не хочу. В RTL8195 я передаю client.write(vfrmbuf, len) размерами в несжатый видео кадр (сотни килобайт) и всё - ok.

Если передавать по одному пакету, то будет тормоз в TCP для Chrome и других.
Надо передавать несколько TCP пакетов, тогда задержки ACK в 200 мс не будет. Так работает TCP стек миромягких и у других.
 
Последнее редактирование:

unreg

Member
pvvx
Можете прояснить - это другая библа? Почему "недо-serverClients.write()"? Еще когда начинал работать с ардуинами, сразу были сомнения в качестве библиотек, некоторые дописывал/переделывал. Вот с ESP столкнулся впервые, поэтому куча пробелов.
 

pvvx

Активный участник сообщества
pvvx
Можете прояснить - это другая библа? Почему "недо-serverClients.write()"? Еще когда начинал работать с ардуинами, сразу были сомнения в качестве библиотек, некоторые дописывал/переделывал. Вот с ESP столкнулся впервые, поэтому куча пробелов.
Ну вы сами пишите, что в Arduino надо всё доделывать или переделывать. Там очень редко бывают правильно реализованные функции... Тем более у ESP SDK, на котором построена Arduino, с закрытыми исходниками, а сам производитель ESP8266 Arduino не выпускал... Да и версия SDK там древняя.
Правда 5 дней назад там поменяли либы SDK на новые, с новыми глюками :)

У вас в коде возможны переполнения стека - буфера в стеке. Стек CPU мал у ESP8266 и затирает переменные для функций ROM-BIOS...
Размер стека на всё у ESP8266 от конца RAM до rom_bss_end = 5328 bytes.
В WiFiClient.h описано: [inline]#define WIFICLIENT_MAX_PACKET_SIZE 1460[/inline]
Размер принятых LwIP данных в TCP стек объявлен в TCP_WND и обычно там от 4*TCP_MSS (1460*4).
Что там возвращает функция в максимуме client.available() я не смотрел, но и два раза по 1460 в стеке CPU вполне хватит, чтобы его переполнить...
Алгоритмы в вашем "скетче" и распределение по памяти выбраны неверно и глюков будет кучка :)
 
Последнее редактирование:

unreg

Member
Что там возвращает функция в максимуме client.available() я не смотрел
Зато я посмотрел :) Ровно столько сколько передано с терминала (отправлял 5 кило с копейками - краш 100%). Обновлю sdk и попробую опять наступить на грабли :) Как думаете, стоит количество доступных байт делить на более меньшие пакеты и вычитывать? И так и не понял по поводу ссылки на вашу библу... она для esp вообще? А то обгуглился и только больше запутался :) Да и по поводу алгоритмов, не совсем догнал - в чем не верность, вроде все согласно референсам....
 

unreg

Member
елы-палы. еще добавлю. отправляю пакет больше 2К. первый avalable = 1460 ! , следующий - остатки. Отправляю более 3,5К. первый доступный все также 1460, следующий - тоже остатки но уже больше 2K.
 

kab

New member
pvvx
Можете прояснить - это другая библа? Почему "недо-serverClients.write()"? Еще когда начинал работать с ардуинами, сразу были сомнения в качестве библиотек, некоторые дописывал/переделывал. Вот с ESP столкнулся впервые, поэтому куча пробелов.
@pvvx
Так Вам и придется, чтобы не переписывать одно и тоже из одного места в другое - написать отдельный топик с подробными пояснениями, что не так как в самой ESP8266, так и в программном обеспечении Expressif @ Arduino :)
 

pvvx

Активный участник сообщества
Так Вам и придется, чтобы не переписывать одно и тоже из одного места в другое - написать отдельный топик с подробными пояснениями, что не так как в самой ESP8266, так и в программном обеспечении Expressif @ Arduino :)
Что там не так в самой Arduino знают все. Но некоторым это нравится.

Зачем нужен FAQ к игре детей в игру про взрослых? :confused:
Вот он -> Arduino/POLICY.md at master · esp8266/Arduino · GitHub :)
Указаны правила игры, общий смысл которых - что это великая Arduino, а в неверном использовании виновен пользователь.
 
Последнее редактирование:

pvvx

Активный участник сообщества
елы-палы. еще добавлю. отправляю пакет больше 2К. первый avalable = 1460 ! , следующий - остатки. Отправляю более 3,5К. первый доступный все также 1460, следующий - тоже остатки но уже больше 2K.
Вот и дробите на кусочки. Тормоз в передаче у такой системы уже встроен программной реализацией основных функций Arduino. Уже устал говорить - задачи у Arduino = переключение светодиода до пары раз в секунду в пределе.
Распределяйте в heap буфер и через него копируйте.
 

unreg

Member
покоцал пакет по 1460 байт, прожевывает нормально. засунул 10К - сдох терминал на сотике. итоговый скетч:
Код:
#include <ESP8266WiFi.h>
#include <WiFiClient.h>

byte mac[] = {
    0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
IPAddress local_IP(192, 168, 4, 22);
IPAddress gateway(192, 168, 4, 9);
IPAddress subnet(255, 255, 255, 0);

#define MAX_SRV_CLIENTS 5

WiFiServer server(23);
WiFiClient serverClients[MAX_SRV_CLIENTS];


void setup()
{


    Serial.begin(115200);

    WiFi.mode(WIFI_AP);
    Serial.print("Setting soft-AP configuration XXX... ");
    Serial.println(WiFi.softAPConfig(local_IP, gateway, subnet) ? "Ready" : "Failed!");

    Serial.print("Setting soft-AP ... ");
    Serial.println(WiFi.softAP("ZAZ", "123456789") ? "Ready" : "Failed!");

    Serial.print("Soft-AP IP address = ");
    Serial.println(WiFi.softAPIP());

    server.begin();
    server.setNoDelay(true);

}

void loop()
{

    uint8_t i;

    if (server.hasClient()) {
        for (i = 0; i < MAX_SRV_CLIENTS; i++) {
            if (!serverClients[i] || !serverClients[i].connected()) {
                if (serverClients[i]) serverClients[i].stop();
                serverClients[i] = server.available();
                Serial1.print("New client: "); Serial1.print(i);
                continue;
            }
        }

        WiFiClient serverClient = server.available();
        serverClient.stop();
    }

    for (i = 0; i < MAX_SRV_CLIENTS; i++) {
        if (serverClients[i] && serverClients[i].connected()) {
            if (serverClients[i].available()) {
                int len = serverClients[i].available();
                while (len !=0 )
                {
                    Serial.println(len);
          if (len > 1460 )//magic number
            len = 1460;
                    uint8_t sbuf[len];

                    serverClients[i].readBytes(sbuf, len);
       
                    for (int j = 0; j < MAX_SRV_CLIENTS; j++)
                    {
                        if (serverClients[j] && serverClients[j].connected())
                        {
                            serverClients[j].write(sbuf, len);
                            //delay(1);
                        }
                    }
         len = serverClients[i].available();
                }
            }
        }
    }
}
 

Алексей.

Active member
Пытался повторить тест чтоб получить ограничения на размер посылки,
так у меня результат обратный получился.
Я далек от сборки на ардуине, у меня совсем простой тест на rtos получился.
esp8266 работает в режиме точки доступа.
Для каждого клиента есть
- очередь куда попадают данные для отправки в сокет
- таск на прием данных из сокета и отправку этих данных в очереди других клиентов
- таск на передачу данных из очереди в сокет
Ограничения с какими я столкнулся, связаны только с размером хипа.
на ПК стартую двух клиентов telnet и запускаю простое приложение, в котором устанавливаем соединение, отправляем в сокет строку в 10 колобайт, выполняем sleep на 3 секунды и разрываем соединение. В запущенных двух клиентах telnet вижу отправленную строку из приложения.
 

pvvx

Активный участник сообщества
Пытался повторить тест чтоб получить ограничения на размер посылки,
так у меня результат обратный получился.
Я далек от сборки на ардуине, у меня совсем простой тест на rtos получился.
esp8266 работает в режиме точки доступа.
Для каждого клиента есть
- очередь куда попадают данные для отправки в сокет
- таск на прием данных из сокета и отправку этих данных в очереди других клиентов
- таск на передачу данных из очереди в сокет
Ограничения с какими я столкнулся, связаны только с размером хипа.
на ПК стартую двух клиентов telnet и запускаю простое приложение, в котором устанавливаем соединение, отправляем в сокет строку в 10 колобайт, выполняем sleep на 3 секунды и разрываем соединение. В запущенных двух клиентах telnet вижу отправленную строку из приложения.
Стандартный socket так и должен работать. В Arduino IDE ESP8266 socket-та и RTOS нет.
Если используется socket от LwIP, то могут быть ограничения при установках конфигурации для LwIP - использовать статические буфера. Если включены динамические, то ограничения снимаются и границы обусловлены размером "heap".

Если работать напрямую с LwIP, то объем передачи статических данных в самодельном socket может быть неограничен. К примеру указываем передать всю область отображения Flash... Буфер нужен только на объем одного TCP пакета сформированного LwIP для передачи драйверу WiFi. У драйвера WiFi обычно есть свои буфера на несколько пакетов.
При передаче LwIP данных для отсылки так-же существует флаг - буферизировать или нет блок данных.
 
Последнее редактирование:
Сверху Снизу