• Уважаемые посетители сайта esp8266.ru!
    Мы отказались от размещения рекламы на страницах форума для большего комфорта пользователей.
    Вы можете оказать посильную поддержку администрации форума. Данные средства пойдут на оплату услуг облачных провайдеров для сайта esp8266.ru
  • Система автоматизации с открытым исходным кодом на базе 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 данных для отсылки так-же существует флаг - буферизировать или нет блок данных.
 
Последнее редактирование:
Сверху Снизу