Чтение сокета часто возвращает -1, пока буфер не пуст

#sockets #arduino #wifi #esp32 #arduino-esp8266

#сокеты #arduino #wifi #esp32 #arduino-esp8266

Вопрос:

Я пытаюсь протестировать передачу данных Wi-Fi между мобильным телефоном и Esp32 (Arduino), когда ESP32 считывает данные файла через Wi-Fi, даже если в нем все еще есть данные, client.read() часто возвращает -1, я должен добавить другие условия, чтобы проверить, завершено чтение или нет.

Мой вопрос в том, почему так много неудачных чтений, любые идеи высоко ценятся.

 void setup()
{
i=0;
Serial.begin(115200);
Serial.println("begin...");

// You can remove the password parameter if you want the AP to be open.
WiFi.softAP(ssid, password);
IPAddress myIP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(myIP);
server.begin();

Serial.println("Server started");
}

// the loop function runs over and over again until power down or reset
void loop()
{

WiFiClient client = server.available();   // listen for incoming clients

if(client)                                // if you get a client,
{
    
    Serial.println("New Client.");           // print a message out the serial port
    Serial.println(client.remoteIP().toString());


    while(client.connected())               // loop while the client's connected
    {
        while(client.available()>0)                // if there's bytes to read from the client,
        {
            char c = client.read();             // read a byte, then
            if(DOWNLOADFILE ==c){

              pretime=millis();
              uint8_t filename[32]={0};
              uint8_t bFilesize[8];
              long filesize;
              int segment=0;
              int remainder=0;
              uint8_t data[512];
              int len=0;
              int totallen=0;
              
              delay(50);
              len=client.read(filename,32);                  
              delay(50);
              len=client.read(bFilesize,8);
              filesize=BytesToLong(bFilesize);
              segment=(int)filesize/512;
              delay(50);

              i=0;  //succeed times
              j=0; //fail times
               ////////////////////////////////////////////////////////////////////
               //problem occures here, to many "-1" return value
               // total read 24941639 bytes, succeed 49725 times, failed 278348 times
               // if there were no read problems, it should only read 48,715 times and finish.
               //But it total read 328,073 times, including 278,348 falied times, wasted too much time
              while(((len=client.read(data,512))!=-1) || (totallen<filesize))
              {
                if(len>-1) {
                  totallen =len; 
                  i  ;
                }
                else{
                  j  ;
                }
                               
              }
             ///loop read end , too many times read fail//////////////////////////////////////////////////////////////////
              
              sprintf(toClient, "nfile name %s,size %d, total read %d, segment %d, succeed %d times, failed %d timesn",filename,filesize,totallen,segment,i,j);
              Serial.write(toClient);
                
              curtime=millis();

              sprintf(toClient, "time splashed %d ms, speed %d Bpsn", curtime-pretime, filesize*1000/(curtime-pretime));
              Serial.write(toClient);
              
              client.write(RETSUCCESS);                  
            }
            else
            {
              Serial.write("Unknow commandn");
            }
        }
    }
           
            
    // close the connection:
    client.stop();
    Serial.println("Client Disconnected.");
}
  

Ответ №1:

Когда вы вызываете available () и проверяете> 0, вы проверяете, есть ли один или несколько символов, доступных для чтения. Это будет верно, если прибыл только один символ. Вы читаете один символ, и это нормально, но затем вы начинаете читать дальше, не останавливаясь, чтобы посмотреть, есть ли еще доступные.

TCP не гарантирует, что если вы запишете 100 символов в сокет, все они поступят одновременно. Они могут поступать произвольными «порциями» с произвольными задержками. Все, что гарантировано, это то, что они в конечном итоге поступят по порядку (или, если это невозможно из-за проблем с сетью, соединение будет прервано.)

В отсутствие функции блокирующего чтения (я не знаю, существуют ли они) вы должны сделать что-то вроде того, что вы делаете. Вы должны читать по одному символу за раз и добавлять его в буфер, изящно передавая возможность получения -1 (следующего символа еще нет, или соединение разорвано). В общем, вы никогда не захотите пытаться прочитать несколько символов за одно чтение (buf, len), если вы только что не использовали available(), чтобы убедиться, что символы len действительно доступны. И даже это может привести к сбою, если ваши буферы действительно большие. Придерживайтесь одного символа за раз.

Разумно вызывать delay(1), когда функция available() возвращает 0. В тех местах, где вы пытаетесь угадать что-то вроде delay (20) перед чтением буфера, вы бросаете кости — нет никаких обещаний, что любая задержка гарантирует доставку байтов. Пример: возможно, капля воды упала на антенну чипа, и она не будет работать, пока капля не испарится. Данные могут задерживаться на несколько минут.

Я не знаю, как ведет себя функция available() в случае сбоя соединения. Возможно, вам придется выполнить read() и получить обратно -1, чтобы диагностировать неудачное соединение. Документация Arduino абсолютно ужасна, поэтому вам придется поэкспериментировать.

TCP намного проще обрабатывать на платформах, которые имеют потоки, блокирующие чтение, select() и другие инструменты для управления данными. Наличие только неблокирующего чтения усложняет задачу, но это так.

В некоторых ситуациях UDP на самом деле намного проще — есть больше гарантий получения сообщений определенного размера в одном блоке. Но, конечно, целые сообщения могут пропадать или отображаться не по порядку. Это компромисс.