2014-12-26 3 views
0

Я получаю данные от клиента и сохраняю его на локальном диске на локальном хосте. Я проверил его на файл с 221 МБ, но тест для файла 1Gb дает следующее исключение:System.OutOfMemoryException на стороне сервера для файлов-клиентов

необработанное исключение типа «System.OutOfMemoryException» произошло в mscorlib.dll

Ниже приводится код на стороне сервера, где исключение из стеблей.

ОБНОВЛЕНО

Сервер:

 public void Thread() 
     { 
      TcpListener tcpListener = new TcpListener(ipaddr, port); 
      tcpListener.Start(); 
      MessageBox.Show("Listening on port" + port);  
      TcpClient client=new TcpClient(); 
      int bufferSize = 1024; 
      NetworkStream netStream; 
      int bytesRead = 0; 
      int allBytesRead = 0; 

      // Start listening 
      tcpListener.Start(); 

      // Accept client 
      client = tcpListener.AcceptTcpClient(); 
      netStream = client.GetStream(); 

      // Read length of incoming data to reserver buffer for it 
      byte[] length = new byte[4]; 
      bytesRead = netStream.Read(length, 0, 4); 
      int dataLength = BitConverter.ToInt32(length,0); 

     // Read the data 
      int bytesLeft = dataLength; 
      byte[] data = new byte[dataLength]; 

      while (bytesLeft > 0) 
      { 

       int nextPacketSize = (bytesLeft > bufferSize) ? bufferSize : bytesLeft; 

       bytesRead = netStream.Read(data, allBytesRead, nextPacketSize); 
       allBytesRead += bytesRead; 
       bytesLeft -= bytesRead; 

      } 

      // Save to desktop 
      File.WriteAllBytes(@"D:\LALA\Miscellaneous\" + shortFileName, data); 

      // Clean up 
      netStream.Close(); 
      client.Close(); 

    } 

Я получаю размер файла первой со стороны клиента за которым следуют данные.

1). Должен ли я увеличить размер буфера или любую другую технику?

2). File.WriteAllBytes() и File.ReadAllBytes() кажется блокирующим и зависающим на ПК. Там есть какой-либо метод асинхронизации, чтобы помочь обеспечить прогресс файла, полученного на стороне сервера.

+1

Вы назначаете массив байтов емкостью 1 ГБ.Неудивительно, что у вас заканчивается память на посредственной машине. Запишите данные в файл chunk-by-chunk, используя поток. –

+1

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

+0

как я могу рассчитать прогресс в этот раз? @DavidLibido – Khan

ответ

2

Перед записью на диск вам не нужно читать все это в памяти. Просто скопируйте прямо из сети потока в FileStream:

byte[] length = new byte[4]; 
// TODO: Validate that bytesRead is 4 after this... it's unlikely but *possible* 
// that you might not read the whole length in one go. 
bytesRead = netStream.Read(length, 0, 4); 
int bytesLeft = BitConverter.ToInt32(length,0); 

using (var output = File.Create(@"D:\Javed\Miscellaneous\" + shortFileName)) 
{ 
    netStream.CopyTo(output, bytesLeft); 
} 

Обратите внимание, что вместо вызова netStream.Close() в явном виде, вы должны использовать using заявление:

using (Stream netStream = ...) 
{ 
    // Read from it 
} 

Таким образом, поток будет закрыт, даже если исключение.

+0

Спасибо за быстрый ответ ... CopyToAsync в .NET4.5 выполняет ту же самую цель .., которую я должен предпочесть для оптимальной производительности? – Khan

+1

@Khan: Ох, я не заметил, что 'CopyTo' /' CopyToAsync' имеют перегрузки, которые принимают количество байтов для копирования - я изменил свой ответ соответствующим образом. Что касается того, хотите ли вы «CopyTo» или «CopyToAsync» ... асинхронная версия не будет поддерживать зависание нити во время копирования, но вам действительно нужно решить, будете ли вы делать весь свой код (или, по крайней мере, обработку весь запрос) асинхронно или нет. Обычно не рекомендуется составлять * одну * часть вашего асинхронного кода. У нас недостаточно контекста, чтобы давать советы на этом фронте. –

+0

Если бы я смог сделать часть (где происходит чтение/запись) асинхронного кода, как бы не было приятно сэр – Khan

1

CLR имеет ограничение на объект, немного меньше 2 ГБ. Однако это теория, на практике, сколько памяти, которую вы можете выделить, зависит от того, сколько памяти фреймворк позволяет вам выделять. Я бы не ожидал, что он позволит вам выделить таблицу данных 1 ГБ. Вы должны выделить меньшую таблицу и записать данные в куски в файл диска.

+2

В наши дни 64-разрядная версия CLR поддерживает большие массивы, если вы включите ее, и возможность выделить 1 ГБ не * полностью * абсурдно ... но явно лучше передать ее –

1

Исключение «из памяти» происходит из-за того, что вы пытаетесь поместить весь файл в память перед тем, как сбросить его на диск. Это субоптимально, потому что вам не нужен весь файл в памяти для записи в файл: вы можете читать его поблочно с шагом по размеру и записывать его по ходу дела.

Начиная с .NET 4.0 вы можете использовать Stream.CopyTo метод для достижения этой цели в несколько строк кода:

// Read and ignore the initial four bytes of length from the stream 
byte[] ignore = new byte[4]; 
int bytesRead = 0; 
do { 
    // This should complete in a single call, but the API requires you 
    // to do it in a loop. 
    bytesRead += netStream.Read(ignore, bytesRead, 4-bytesRead); 
} while (bytesRead != 4); 
// Copy the rest of the stream to a file 
using (var fs = new FileStream(@"D:\Javed\Miscellaneous\" + shortFileName, FileMode.Create)) { 
    netStream.CopyTo(fs); 
} 
netStream.Close(); 

Начиная с .NET 4.5 вы можете использовать CopyToAsync, тоже, что даст вам возможность читать и писать асинхронно.

Обратите внимание на код, который отбрасывает первые четыре байта из потока. Это делается для того, чтобы избежать записи длины потока вместе с байтами полезной нагрузки. Если у вас есть контроль над сетевым протоколом, вы можете изменить сторону отправки, чтобы остановить префикс потока с его длиной и удалить код, который читает и игнорирует его на принимающей стороне.

+1

Это скопирует поток * whole *. OP знает количество копируемых байтов, и если после этого может быть больше данных для обработки по-разному, вместо этого следует использовать перегрузку, в которой записывается количество байтов для записи –

+1

Возможно. Это зависит ... мы не знаем достаточно о том, какой протокол задействован, или OP контролирует его. Но я бы предложил хотя бы сказать в вашем ответе, что вы принимаете изменения в протоколе - в противном случае OP, по крайней мере, получит неожиданный 4-байтовый префикс ... –

+0

@dasblinkenlight Что означает «netStream.CopyTo (fs)», который будет читать все отправляющие байты, если я изменю префикс отправки – Khan

 Смежные вопросы

  • Нет связанных вопросов^_^