服务器端的System.OutOfMemoryException用于客户端文件

时间:2014-12-26 11:19:01

标签: c# winforms sockets client-server networkstream

我从客户端获取数据并将其保存到本地主机上的本地驱动器。我检查了一个221MB的文件但是对1Gb文件的测试给出了以下异常:

  

未处理的类型' 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()似乎阻塞并冻结了PC。是否有任何异步方法可以帮助提供服务器端收到的文件进度。

3 个答案:

答案 0 :(得分: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
}

这样即使抛出异常,流也将被关闭。

答案 1 :(得分:1)

CLR的每个对象限制有点短于2GB。然而,这就是理论,实际上你可以分配多少内存取决于框架允许你分配多少内存。我不希望它允许您分配1 GB数据表。您应该分配较小的表,并将数据以块的形式写入磁盘文件。

答案 2 :(得分: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,这将为您提供一种异步读写的方法。

请注意从流中删除初始四个字节的代码。这样做是为了避免写入流的长度以及"有效载荷"字节。如果您可以控制网络协议,则可以更改发送方以停止使用其长度为流添加前缀,并删除在接收方读取和忽略它的代码。