2014-12-26 2 views
0

클라이언트에서 데이터를 가져 와서 로컬 호스트의 로컬 드라이브에 저장하고 있습니다. 221MB의 파일을 검사했지만 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()이 차단되어 PC가 멈추는 것처럼 보입니다. 서버 쪽에서받은 파일의 진행 상태를 알려주는 비동기 메소드가 있습니다.

+1

1GB 바이트 배열을 할당하고 있습니다.평범한 컴퓨터에서 메모리가 부족한 것도 당연합니다. 스트림을 사용하여 chunk-by-chunk 파일에 데이터를 씁니다. –

+1

전체 파일을 메모리에 보관하지 마십시오. 파일 스트림을받은 즉시 파일 스트림을 사용하여 바이트를 디스크에 씁니다. 또한 그 시점에서 진행 상황을 계산할 수 있습니다. –

+0

어떻게 그 시간을 계산할 수 있습니까? @DavidLibido – Khan

답변

2

디스크에 기록하기 전에 전체 내용을 메모리로 읽을 필요가 없습니다. 그냥 FileStream에 네트워크 스트림에서 직접 복사하는 대신 명시 적으로 netStream.Close()를 호출, 당신이 using 문을 사용한다

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); 
} 

참고 :

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

스트림이 경우에도 종료됩니다 그런 식으로 예외가 throw됩니다.

+0

신속한 답변 주셔서 감사 ... .NET4.5의 CopyToAsync는 동일한 목적을 제공합니다. 어느 것이 성능 최적화를 위해 선호해야합니까? – Khan

+1

@Khan : 오, 나는'CopyTo' /'CopyToAsync'에 복사 할 바이트 수를 차지하는 과부하가 있음을 눈치 채지 못했습니다. 나는 내 대답을 적절하게 변경했습니다. 'CopyTo' 또는'CopyToAsync'를 원한다면 ... 비동기 버전은 복사하는 동안 쓰레드를 매달아 두지 않을 것이지만, 전체 코드를 작성하는지 여부를 결정해야합니다. 전체 요청) 비동기 또는 아닙니다. 일반적으로 코드 비동기의 일부만을 만드는 것은 좋지 않습니다. 우리는 그 전선에 대한 충고를하기에 충분한 맥락이 없습니다. –

+0

코드 비동기의 일부분 (읽기/쓰기가 일어나는 부분)을 어떻게 만들 수 있다면 좋을까요? 선생님 – Khan

1

CLR은 개체 당 제한이 2GB 미만입니다. 그러나 이것이 이론입니다. 실제로 할당 할 수있는 메모리 양은 프레임 워크가 할당 할 수있는 메모리 양에 따라 다릅니다. 나는 1GB 데이터 테이블을 할당 할 수 있기를 기대하지는 않는다. 더 작은 테이블을 할당하고 데이터를 청크로 디스크 파일에 기록해야합니다.

+2

요즘에는 64 비트 CLR을 켜면 더 큰 배열을 지원할 수 있습니다. 1GB를 할당 할 수 있다는 것은 완전히 불합리한 것은 아니지만 스트리밍하는 것이 더 낫습니다. –

1

디스크에 덤프하기 전에 전체 파일을 메모리에 저장하려고하기 때문에 "메모리 부족"예외가 발생합니다. 파일에 쓸 때 전체 파일을 메모리에 저장할 필요가 없기 때문에 차선책입니다. 블록 단위로 블록 단위로 읽을 수 있으며 이동 도중 쓸 수 있습니다. 당신이 CopyToAsync을 사용할 수 있습니다 당신이 할 수있는 방법을 줄 것이다, 너무,

// 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을 시작으로 : .NET 4.0을 시작으로

당신은 몇 줄의 코드에서 이러한 목표를 달성하기 위해 Stream.CopyTo 방법을 사용할 수 있습니다 비동기 적으로 읽고 쓰는 일.

스트림에서 처음 4 바이트를 떨어 뜨리는 코드에 유의하십시오. 이는 "페이로드"바이트와 함께 스트림의 길이를 쓰는 것을 피하기 위해 수행됩니다. 네트워크 프로토콜을 제어 할 수있는 경우 송신 측에서 스트림의 길이를 접두어로 사용하지 않도록 변경하고 수신 측에서 읽고 무시하는 코드를 제거 할 수 있습니다.

+1

그건 * 전체 * 스트림을 복사 할 것입니다 ... OP는 복사 할 바이트 수를 알고 있고, 이후에 다르게 처리 할 데이터가 더 많이있는 경우 쓰기 바이트 수를 사용하는 오버로드를 대신 사용해야합니다 . –

+1

어쩌면. 그것은 ... 어떤 프로토콜이 관련되어 있는지, 또는 OP가 그것을 제어하고 있는지에 대해 충분히 알지 못합니다. 그러나 적어도 당신의 답변에서 당신은 프로토콜의 변경을 가정하고 있다고 말하고 싶습니다. 그렇지 않으면 OP는 적어도 예기치 않은 4 바이트 프리픽스를 얻습니다. –

+0

@dasblinkenlight 만약 내가 접두사를 바꾼다면 모든 전송 바이트를 읽을'netStream.CopyTo (fs)'의 보증은 무엇입니까 – Khan

관련 문제