2016-07-01 2 views
3

У нас есть требование загрузить большие файлы прошивки на принтеры для обновления прошивки устройства. Устройство принтера находится в той же сети, что и мой сервер, и размер прошивки, которую мы пытаемся загрузить, составляет примерно 200 - 500 МБ. Подход, который мы выбрали, заключается в загрузке прошивки (.bin-файла) в поток Памяти и записи ее в куски по сети с использованием TcpClient.Лучший способ записи больших данных по сетевому потоку TcpClient

Основываясь на ответе сетевого потока, мы показываем статус обновления прошивки для клиента. Ниже приведен фрагмент кода, который мы использовали для обновления прошивки. Я хочу знать, лучший ли это подход, поскольку неправильный может нанести вред устройству.

EDIT:

class MyClass 
{ 
    int port = 9100; 
    string _deviceip; 
    byte[] m_ReadBuffer = null; 
    TcpClient _tcpclient; 
    NetworkStream m_NetworkStream; 
    static string CRLF = "\r\n"; 

    public event EventHandler<DeviceStatus> onReceiveUpdate; 

    public async Task<bool> UploadFirmware(Stream _stream) 
    { 
     bool success = false; 
     try 
     { 
      _tcpclient = new TcpClient(); 
      _tcpclient.Connect(_deviceip, port); 

      _stream.Seek(0, SeekOrigin.Begin); 
      m_NetworkStream = _tcpclient.GetStream(); 
      byte[] buffer = new byte[1024]; 
      m_ReadBuffer = new byte[1024]; 
      int readcount = 0; 
      m_NetworkStream.BeginRead(m_ReadBuffer, 0, m_ReadBuffer.Length, 
            new AsyncCallback(EndReceive), null); 
      await Task.Run(() => 
      { 
       while ((readcount = _stream.Read(buffer, 0, buffer.Length)) > 0) 
       { 
        m_NetworkStream.Write(buffer, 0, readcount); 
        m_NetworkStream.Flush(); 
       } 
      }); 
      success = true; 
     } 
     catch (Exception ex) 
     { 
      upgradeStatus = false; 
     } 
     return success; 
    } 

    private void EndReceive(IAsyncResult ar) 
    { 
     try 
     { 
      int nBytes; 
      nBytes = m_NetworkStream.EndRead(ar); 
      if (nBytes > 0) 
      { 
       string res = Encoding.UTF8.GetString(m_ReadBuffer, 0, nBytes); 
       DeviceStatus status = new DeviceStatus(); 

       string[] readlines = res.Split(new string[] { CRLF }, 
           StringSplitOptions.RemoveEmptyEntries); 
       foreach (string readline in readlines) 
       { 
        if (readline.StartsWith("CODE")) 
        { 
         //read readline string here 
         break; 
        } 
       } 
      } 

      if (m_NetworkStream.CanRead) 
      { 
       do 
       { 
        m_NetworkStream.BeginRead(m_ReadBuffer, 0, m_ReadBuffer.Length, new 
              AsyncCallback(EndReceive), null); 
       } while (m_NetworkStream.DataAvailable); 
      } 
     } 
     catch (ObjectDisposedException ods) 
     { 
      return; 
     } 
     catch (System.IO.IOException ex) 
     { 
     } 
    } 
} 

Любая помощь будет очень ценна.

ответ

-2

Поскольку максимальная длина для TcpPacket равна 65535 (2^16-1), если вы отправите какой-либо пакет, пробивающий эту длину, он будет усечен. Если бы я был вами, я думаю, что лучший способ отправки больших пакетов - устанавливать заголовок каждого пакета и перечислять их. Например:
C-> S; [P1] <content>
и тогда такая же структура, просто плюс 1 [P2] <content>
Для этого просто используйте несколько подстрок, чтобы усечь данные и отправить их.

Cheers!

+0

Я не посылаю его сразу. Возможно, вы не заметили, я отправляю данные в куски во время цикла. – Saket

+0

TCP не предоставляет семантику пакета.Это непрерывный поток байтов, и приложения не могут сделать ничего плохого, отправив определенные пакеты, потому что приложения вообще не отправляют пакеты. – usr

2

Ваш код в основном хорошо с несколькими вопросами:

  1. m_NetworkStream.Flush(); AFAIK это не делает ничего. Если бы он сделал что-то, это повредило бы пропускную способность. Так что удалите это.
  2. _stream.Seek(0, SeekOrigin.Begin); поиск вызывающих проблем, удалить это. Это нарушение наложения.
  3. Используйте более крупные буферы. Экспериментально определите правильный размер. Обычно я начинаю с 64 КБ для массовых переводов. Это делает МО менее болтливым.
  4. Включите наглывание, которое помогает с массовыми передачами, потому что оно избавляет вас от ложных небольших пакетов.
  5. Вы можете заменить весь цикл чтения-записи на Stream.Copy.
  6. То, как вы сообщаете об исключениях вызывающим, скрывает много информации. Просто дайте исключению пузырь. Не возвращайте bool.
  7. Используйте using для всего ресурса, чтобы обеспечить их очистку в случае ошибки.
  8. nBytes = m_NetworkStream.EndRead(ar); здесь вы предполагаете, что одно чтение вернет все данные, которые будут поступать. Однако вы можете получить только первый байт. Возможно, вы должны использовать StreamReader.ReadLine в цикле, пока не узнаете, что все готово.
  9. catch (System.IO.IOException ex) { } Что это значит? Если обновления прошивки - такая критическая вещь, то подавление ошибок кажется очень опасным. Как еще вы можете узнать об ошибках?
  10. Я бы преобразовал код чтения, чтобы использовать await.
+1

Большое спасибо за предоставление такого подробного анализа. Однако я смущен в реализации восьмого пункта. Вы имеете в виду, что я должен использовать цикл внутри моего метода EndReceive? Я буду очень признателен, если вы уточните этот момент. – Saket

+1

Я больше думал об этом: 'Task.Run (() => {while (true) var line = myStreamReader.ReadLine(); if (IsEnd (строка)) break; else ProcessLine (строка);});' , Это идея, пожалуйста, посмотрите, можете ли вы сделать эту работу для вас. Вы можете удалить весь мусор Begin/End. Шаблон APM устарел и с трудом поддается обработке. – usr

+1

'IsEnd' может быть' return line == null || line.StartsWith ("CODE") 'если я правильно понимаю ваше намерение. – usr