Запись файла с SD - карты на ethernet-клиент прекращает отправку примерно через 2980 символов, плата STM32f7

Я пытаюсь записать файл, найденный на SD-карте, в подключенный клиент Ethernet. Файл открывается нормально, и у нас нет проблем с написанием клиенту. В целях тестирования я также записываю строки, считанные из файла в последовательный порт, одновременно с записью на клиент. Однако происходит то, что все содержимое файла отображается в последовательном мониторе, но браузер получает только около 2978 символов. фактический файл, не включая заголовок, отправленный отдельно, составляет 4426 байт. Я провел тест, отправив около 260000 через 8000 "client.print (...)", и это сработало отлично.

Я приложил код. Все, что не является необходимым для основной функции кода, было удалено, чтобы дать нам минимум, необходимый для отладки. Я использую библиотеки EthernetWebServer_SSL_STM32 и SdFat. Мой код взят из примера веб - сервера.

/****************************************************************************************************************************
  WebServer.ino - Simple Arduino web server sample for ESP8266 AT-command shield
  For STM32F/L/H/G/WB/MP1 with built-in Ethernet LAN8742A (Nucleo-144, DISCOVERY, etc) or W5x00/ENC28J60 shield/module
  
  EthernetWebServer_SSL_STM32 is a library for STM32 using the Ethernet shields to run WebServer and Client with/without SSL
  Use SSLClient Library code from https://github.com/OPEnSLab-OSU/SSLClient
  
  Built by Khoi Hoang https://github.com/khoih-prog/EthernetWebServer_SSL_STM32
  Licensed under MIT license
 *****************************************************************************************************************************/
#define REPLY_HEADER    "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n"
    
    void Process_Client_Request()
    {
        FsFile      file;
        char        line[500];
        char        Received_Character = '\0';
        uint32_t    Character_Index    =   0;
        uint32_t    n;
        uint32_t    File_Length_Bytes;
        uint32_t    File_Length_Lines;
    
        // listen for incoming clients
        EthernetClient client = server.available();
        
    //  static  ArduinoOutStream sout(client);
        static  ArduinoOutStream cout(Serial);
    
        
        if (client)
        {
            // an http request ends with a blank line
            bool currentLineIsBlank = true;
            
            while (client.connected())
            {
                if (client.available())
                {
                    char c = client.read();
                    Serial.write(c);
                    // if you've gotten to the end of the line (received a newline
                    // character) and the line is blank, the http request has ended,
                    // so you can send a reply
                    if (c == '\n' && currentLineIsBlank)
                    {
                        Serial.println(F("Sending response:\n"));
                        
                        // send a standard http response header
                        // use \r\n instead of many println statements to speedup data send
                        client.print(REPLY_HEADER);
                        Serial.println(F(REPLY_HEADER));
                        
                        // Open the SD card
                        if (!sd.begin(SD_CONFIG)) 
                        {
                            sd.initErrorHalt(&Serial);
                        }
                        
                        // Specify the file to read
                        char fileName[] = "404.htm";
                        
                        // Open the file to READ from the SD card
                        if (!file.open(fileName, O_READ)) 
                        {
                            sd.errorHalt(&Serial, F("open failed"));
                        }
                        
                        File_Length_Bytes = file.fileSize();
                        File_Length_Lines = 0;
    
                        Serial.print(__DATE__); Serial.print(", "); Serial.println(__TIME__);
    
                        while ((n = file.fgets(line, sizeof(line))) > 0)
                        {
                            // Write the line to the client
                            client.print(line);
    
                            // Print the line
                            Serial.print(line);
    
                            File_Length_Lines++;
                        }
                        file.close();
                        break;
                    }
                    
                    if (c == '\n')
                    {
                        // you're starting a new line
                        currentLineIsBlank = true;
                    }
                    
                    else if (c != '\r')
                    {
                        // you've gotten a character on the current line
                        currentLineIsBlank = false;
                    }
                }
            }
            // give the web browser time to receive the data
            delay(10);
            
            // close the connection:
            client.stop();
            Serial.println(F("\n\nClient disconnected"));
            
            Serial.println("\n");
            Serial.print("Total bytes read: "); Serial.println(File_Length_Bytes);
            Serial.print("Total lines read: "); Serial.println(File_Length_Lines);
            Serial.println("");
        }   
    }

Есть какие-нибудь идеи, что я делаю не так?

Я попытался удалить все ссылки на SD-карту и жестко закодировал фразу для отправки через цикл while () - это сработало отлично. Пожалуйста, смотрите код ниже:

#define REPLY_HEADER    "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n"

void Process_Client_Request()
{
    uint32_t    File_Length_Bytes;
    uint32_t    File_Length_Lines;
    uint16_t    c;
    uint32_t    LineCount = 0;

    // listen for incoming clients
    EthernetClient client = server.available();
    
      // Open serial communications and wait for port to open:
      Serial.begin(115200);
      while (!Serial);

    
    if (client)
    {
        // an http request ends with a blank line
        bool currentLineIsBlank = true;
        
        while (client.connected())
        {
            if (client.available())
            {
                char c = client.read();
                Serial.write(c);
                // if you've gotten to the end of the line (received a newline
                // character) and the line is blank, the http request has ended,
                // so you can send a reply
                if (c == '\n' && currentLineIsBlank)
                {
                    Serial.println(F("Sending response:\n"));
                    
                    // send a standard http response header
                    // use \r\n instead of many println statements to speedup data send
                    client.print(REPLY_HEADER);
                    Serial.println(F(REPLY_HEADER));

                    client.print("</body></html>");
                    Serial.println("</body></html>");

                    while (LineCount < 8000)
                    {
                        client.print("This is line number: "); client.print(++LineCount); client.print("<br>\r\n");
                        Serial.print("This is line number: "); Serial.print(LineCount); Serial.print("<br>\r\n");
                    } 
                    
                    client.print("</html></body>");
                    Serial.println("</html></body>");
                    break;
                }
                
                if (c == '\n')
                {
                    // you're starting a new line
                    currentLineIsBlank = true;
                }
                
                else if (c != '\r')
                {
                    // you've gotten a character on the current line
                    currentLineIsBlank = false;
                }
            }
        }
        // give the web browser time to receive the data
        delay(10);
        
        // close the connection:
        client.stop();
        Serial.println(F("\n\nClient disconnected"));
        
        Serial.println("\n");
    }   
}

При чтении строки текста с SD - карты и печати ее клиенту передача клиенту завершается примерно через 1279 символов плюс-минус. Когда это происходит, веб-браузер продолжает ждать получения дополнительных данных.

, 👍2

Обсуждение

может быть, "файл".fgets(line, sizeof(line), "\n")) или client.write(line, countOfBytesReadWithfget);`?, @Juraj

вы не используете библиотеку EthernetWebServer_SSL_STM32 в рассматриваемом фрагменте кода. это основное использование EthernetServer, как в примере веб-сервера библиотеки Ethernet., @Juraj

Спасибо вам за ваши предложения, Юрай. Я действительно реализовал и то, и другое, но без каких-либо изменений. Я все еще не получаю весь файл целиком., @JackOfAllTrades99

Я думаю, что \n не считается, поэтому чтение заканчивается на пустой строке, @Juraj

Вывод на клиент обычно заканчивается в середине строки. Оказывается, что client.print() перестает отправлять данные после заданного количества символов., @JackOfAllTrades99

Может быть, это связано с размером буфера? https://github.com/OPEnSLab-OSU/SSLClient#implementation-gotchas, @tim

какое оборудование и библиотеку Ethernet вы используете? Вы используете библиотеку SSLclient?, @Juraj

Это стартовое сообщение при запуске кода: Запустите веб-сервер на NUCLEO_F767ZI, используя библиотеку LAN8742A Ethernet и STM32Ethernet EthernetWebServer_SSL_STM32 v1.3.0 Я определил, что код использует файл ethernet.h, найденный здесь: Arduino/libraries/STM32duino_LwIP/src/netif/ethernet.h. Этот файл не имеет возможности увеличить размер буфера, как предлагал Тим. Попробовал просмотреть различные заголовочные файлы, на которые ссылаются ссылки, но так и не смог найти настройку буферизации отправки., @JackOfAllTrades99

Самое смешное, что это только проблема при чтении строки с SD-карты и записи ее клиенту. Если я исключу SD-карту из уравнения и запишу консервированные данные, это работает нормально. Это, кажется, указывает на то, что это не может быть проблемой буфера отправки. У меня кончаются идеи., @JackOfAllTrades99

Просто интересно, не проблема ли это со временем. Версия SD-карты отправляет 500 байт на "печать", в то время как жестко закодированная версия отправляет около 30 байт на "печать". В качестве эксперимента попробуйте замедлить внутренний цикл "while" с помощью таймера "millis ()" (возможно, 100 мс на "печать") и увеличить конечную задержку(10) до "delay(1000)" после внешнего цикла "while". Или, может быть, SD-библиотека мешает работе библиотеки Ethernet?, @tim

попробуйте client.write(line, n); вместо client.print(line); (кстати: это не строка), @Juraj

Задержки были хорошей идеей, но, к сожалению, не возымели никакого эффекта. Я уже пробовал использовать client.write, а также потоковую передачу на клиент. Результаты были согласованы для всех этих методов., @JackOfAllTrades99

Я взял жестко закодированный вывод и создал файл на SD-карте с 8000 строк содержимого, т. е., <html><body> Это номер строки: 1<br> Это номер строки: 2<br> Это номер строки: 3<br> ... но самое странное, что здесь происходит, - это то, что распечатка останавливается на 201 строке для печати на последовательный порт и 104 строках для клиента., @JackOfAllTrades99

Это может быть хитроумная SD-карта. Библиотека, которую вы используете, возвращает/устанавливает коды ошибок для файла.fgets(line, sizeof(line))? Сравните с [fegts](https://www.cplusplus.com/reference/cstdio/fgets/) и [ferror](https://www.cplusplus.com/reference/cstdio/ferror/). Не могли бы вы предоставить ссылку на библиотеки, которые вы используете?, @tim

это ссылка на библиотеку, которую я использую для функций SD-карты: https://github.com/greiman/SdFat ... Утром я снова займусь этим. Спасибо вам за всю вашу помощь до сих пор!, @JackOfAllTrades99

Что касается SD - карты, то я пробовал переключаться между двумя различными марками и размерами SD-карт с разными форматами-результат был одинаковым между ними. Как правило, я использую только карты SanDisk Ultra, так как они доказали свою надежность на протяжении многих лет. Fgets() генерирует общий (-1) код ошибки. Я проверил это, и оно не было выброшено. Я также протестировал использование нескольких различных файлов на SD-карте, чтобы исключить возможность внедрения мошеннических кодов в текст. И снова никаких изменений., @JackOfAllTrades99

Странно то, что когда я комментирую client.print и записываю только в серийный номер, нам удается распечатать все строки файлов в серийный номер. Наш вывод преждевременно завершается только в том случае, если мы пишем клиенту. Это похоже на своего рода конфликт между библиотекой Ethernet и библиотекой SdFat., @JackOfAllTrades99

Я попытался использовать стандартную библиотеку Arduino SD вместо библиотеки SdFat. Я также сделал client.write(myFile.read()); вместо чтения в переменную 'line', в остальном код не изменился. Те же результаты - вывод клиенту прекращается примерно после 104 из 8000 переданных строк., @JackOfAllTrades99

Разделите вашу программу на функциональные блоки. Протестируйте каждый блок отдельно. Если все они работают отдельно, добавляйте блок за блоком (2 блока, 3 блока и т. Д.) и тестируйте после каждого добавленного блока. Таким образом, вы можете точно определить, где возникает проблема, и иметь лучшее изменение для ее решения., @PimV

Комментарии не предназначены для расширенного обсуждения; этот разговор был [перенесен на chat](https://chat.stackexchange.com/rooms/128516/discussion-on-question-by-jackofalltrades99-writing-file-from-sd-card-to-etherne)., @Juraj