2013-01-05 3 views
2

내 파일 전송 서비스를 작업 중이므로 WCF 스트리밍으로 파일을 잘 전송할 수 있습니다. 나는 좋은 속도를 얻었고, 스트리밍하기 전에 작은 파일에 내 파일을 덩어리 때문에 좋은 이력서 지원을 할 수있게되었습니다.스트리밍 작업을 수행 할 때 어떻게 세분성을 향상시킬 수 있습니까?

그러나 메시지가 스트리밍 및 기록 될 때 상세한 전송 속도를 측정 할 때 서버 측 전송과 클라이언트 측 수신 모두에서 문제가 발생합니다.

다음은 파일이 청크 된 코드입니다.이 코드는 클라이언트가 다른 청크를 클라이언트에 보내야 할 때마다 서비스에서 호출합니다. 위에서

public byte[] NextChunk() 
    { 
     if (MoreChunks) // If there are more chunks, procede with the next chunking operation, otherwise throw an exception. 
     { 
      byte[] buffer; 
      using (BinaryReader reader = new BinaryReader(File.OpenRead(FilePath))) 
      { 
       reader.BaseStream.Position = currentPosition; 
       buffer = reader.ReadBytes((int)MaximumChunkSize); 
      } 
      currentPosition += buffer.LongLength; // Sets the stream position to be used for the next call. 

      return buffer; 
     } 
     else 
      throw new InvalidOperationException("The last chunk of the file has already been returned."); 

, 나는 기본적으로 내가 사용하고 청크 크기에 따라 버퍼에 기록 (이 경우에는 내가 최고 전송 속도 크거나 작은 덩어리가 크기에 비해이 발견되는 2메가바이트이다). 그런 다음 약간의 작업을 수행하여 내가 중단 한 부분을 기억하고 버퍼를 반환합니다.

다음 코드는 서버 측 작업입니다. 은 WCF 호출의 클라이언트에 의해 호출되는이 방법에서

public FileMessage ReceiveFile() 
    { 
     if (!transferSpeedTimer.Enabled) 
      transferSpeedTimer.Start(); 

     byte[] buffer = chunkedFile.NextChunk(); 

     FileMessage message = new FileMessage(); 
     message.FileMetaData = new FileMetaData(chunkedFile.MoreChunks, buffer.LongLength); 
     message.ChunkData = new MemoryStream(buffer); 

     if (!chunkedFile.MoreChunks) 
     { 
      OnTransferComplete(this, EventArgs.Empty); 

      Timer timer = new Timer(20000f); 
      timer.Elapsed += (sender, e) => 
      { 
       StopSession(); 
       timer.Stop(); 
      }; 
      timer.Start(); 
     } 

     //This needs to be more granular. This method is called too infrequently for a fast and accurate enough progress of the file transfer to be determined. 
     TotalBytesTransferred += buffer.LongLength; 

     return message; 
    } 

, 나는 다음 청크에 대한 정보를 얻을, 전송이 완료되면 내 세션을 중지하는 타이머와 조금을, 내 메시지를 작성하고 전송 속도를 업데이트하십시오. 내가 메시지를 반환하기 직전에, 내 TotalBytesTransferred을 버퍼의 길이로 증가시켜 전송 속도를 계산하는 데 사용됩니다.

이 문제는 파일을 클라이언트로 스트리밍하는 데 시간이 걸리므로 속도가 틀립니다. 여기서 목표로 삼고 자하는 것은 TotalBytesTransferred 변수를 좀 더 세부적으로 수정 한 것이므로 주어진 시간에 얼마나 많은 데이터가 클라이언트에 전송되는지 더 잘 표현할 수 있습니다.

이제는 전송 속도를 완전히 다른 방식으로 계산하는 클라이언트 측 코드입니다. 여기

if (Role == FileTransferItem.FileTransferRole.Receiver) 
     { 
      hostChannel = channelFactory.CreateChannel(); 
      ((IContextChannel)hostChannel).OperationTimeout = new TimeSpan(3, 0, 0); 

      bool moreChunks = true; 
      long bytesPreviousPosition = 0; 

      using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(fileWritePath))) 
      { 
       writer.BaseStream.SetLength(0); 

       transferSpeedTimer.Elapsed += ((sender, e) => 
       { 
        transferSpeed = writer.BaseStream.Position - bytesPreviousPosition; 
        bytesPreviousPosition = writer.BaseStream.Position; 
       }); 
       transferSpeedTimer.Start(); 

       while (moreChunks) 
       { 
        FileMessage message = hostChannel.ReceiveFile(); 
        moreChunks = message.FileMetaData.MoreChunks; 

        writer.BaseStream.Position = filePosition; 

        // This is golden, but I need to extrapolate it out and do the stream copy myself so I can do calculations on a per byte basis. 
        message.ChunkData.CopyTo(writer.BaseStream); 

        filePosition += message.FileMetaData.ChunkLength; 

        // TODO This needs to be more granular 
        TotalBytesTransferred += message.FileMetaData.ChunkLength; 
       } 

       OnTransferComplete(this, EventArgs.Empty); 
      } 
     } 
    else 
     { 
      transferSpeedTimer.Elapsed += ((sender, e) => 
      { 
       totalElapsedSeconds += (int)transferSpeedTimer.Interval; 
       transferSpeed = TotalBytesTransferred/totalElapsedSeconds; 
      }); 
      transferSpeedTimer.Start(); 

      host.Open(); 
     } 

, 내 TotalBytesTransferred 또한 덩어리가 들어오는 길이를 기반으로합니다. 나는, 내가 스트림 자신을 쓰는 대신 스트림의 CopyTo를 사용을 할 경우 내가 좀 더 세부적인 계산을 얻을 수 있습니다 알고 있지만 이 일에 최선을 다하는 방법을 정확히 모르겠다.

아무도 나를 도와 줄 수 있습니까? 이 클래스 밖에서 나는 내부적으로 업데이트 된 TransferSpeed 속성을 폴링하는 또 다른 클래스를 가지고 있습니다.

너무 많은 코드를 게시하면 사과하지만 게시 할 내용과 그렇지 않은 내용을 잘 모릅니다.

편집 : 나는 적어도 서버 측 구현, 내가 전송 된 바이트 수에 대한 더 세부적인 독서를 얻을 수있는 방법은, 스트림의 반환 메시지 값의 위치를 ​​읽는 것입니다 알고 있습니다. 그러나, 나는 나의 카운트에서 절대적인 무결성을 보장하기 위해 이것을하는 방법을 모른다. 스트림을 전송할 때 타이머를 사용하고 위치를 폴링하는 방법에 대해 생각했지만 다음 호출이 이루어질 수 있으며 신속하게 동기화되지 않게됩니다.

반환되는 스트림에서 데이터를 폴링하고 스트림이 끝날 때 즉시 알 수 있으므로 스트림의 나머지 부분을 바이트 수에 빠르게 추가 할 수 있습니까?

답변

0

좋아, 나는 나를 위해 이상적이라고 생각하는 것을 발견했다.나는 그것이 완벽한 지 모른다. 그러나 그것은 나의 필요에 꽤 좋다. Server 측면에서

, 우리는 파일을 전송하는 작업을 수행이 코드가 있습니다. chunkedFile 클래스는 분명히 청크를 처리하지만 이는 정보를 Client으로 보내는 코드입니다.

public FileMessage ReceiveFile() 
    { 
     byte[] buffer = chunkedFile.NextChunk(); 

     FileMessage message = new FileMessage(); 
     message.FileMetaData = new FileMetaData(chunkedFile.MoreChunks, buffer.LongLength, chunkedFile.CurrentPosition); 
     message.ChunkData = new MemoryStream(buffer); 

     TotalBytesTransferred = chunkedFile.CurrentPosition; 
     UpdateTotalBytesTransferred(message); 

     if (!chunkedFile.MoreChunks) 
     { 
      OnTransferComplete(this, EventArgs.Empty); 

      Timer timer = new Timer(20000f); 
      timer.Elapsed += (sender, e) => 
      { 
       StopSession(); 
       timer.Stop(); 
      }; 
      timer.Start(); 
     } 

     return message; 
    } 

는 클라이언트는 기본적으로이 코드를 호출하고 서버는 기본 파일 시스템 파일을 추적하는합니다 (chunkedFile의 위치에 따라 TotalBytesTransferred를 업데이트, 새로운 덩어리를 얻을 스트림에 넣어 진행 데이터를 그리는 데 사용됩니다). 방법 UpdateTotalBytesTransferred(message)을 잠깐 보여 드리겠습니다. 서버와 클라이언트의 모든 코드는 TotalBytesTransferred의보다 세부적인 폴링을 달성하기 위해 상주합니다.

다음 위로는 클라이언트 측의 작품입니다.

  hostChannel = channelFactory.CreateChannel(); 
      ((IContextChannel)hostChannel).OperationTimeout = new TimeSpan(3, 0, 0); 

      bool moreChunks = true; 

      using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(fileWritePath))) 
      { 
       writer.BaseStream.SetLength(0); 

       while (moreChunks) 
       { 
        FileMessage message = hostChannel.ReceiveFile(); 
        moreChunks = message.FileMetaData.MoreChunks; 

        UpdateTotalBytesTransferred(message); 

        writer.BaseStream.Position = filePosition; 

        message.ChunkData.CopyTo(writer.BaseStream); 
        TotalBytesTransferred = message.FileMetaData.FilePosition; 

        filePosition += message.FileMetaData.ChunkLength; 
       } 

       OnTransferComplete(this, EventArgs.Empty); 
      } 

이 코드는 매우 간단합니다. 호스트를 호출하여 파일 스트림을 가져오고 UpdateTotalBytesTransferred(message) 메서드를 사용합니다. 작성중인 기본 파일의 위치를 ​​기억하기 위해 약간의 작업을 수행하고 완료 후 TotalBytesTransferred을 업데이트하면서 해당 파일에 스트림을 복사합니다.

나는 다음과 같이 UpdateTotalBytesTransferred 방법이었다 찾고 있던 단위를 달성하는 방법. 그것은 서버와 클라이언트 모두 똑같이 작동합니다.

private void UpdateTotalBytesTransferred(FileMessage message) 
    { 
     long previousStreamPosition = 0; 
     long totalBytesTransferredShouldBe = TotalBytesTransferred + message.FileMetaData.ChunkLength; 

     Timer timer = new Timer(500f); 

     timer.Elapsed += (sender, e) => 
     { 
      if (TotalBytesTransferred + (message.ChunkData.Position - previousStreamPosition) < totalBytesTransferredShouldBe) 
      { 
       TotalBytesTransferred += message.ChunkData.Position - previousStreamPosition; 
       previousStreamPosition = message.ChunkData.Position; 
      } 
      else 
      { 
       timer.Stop(); 
       timer.Dispose(); 
      } 
     }; 

     timer.Start(); 
    } 

기본적으로 스트림이며 파일 자체에 대한 정보 인 FileMessage를 사용합니다. 변수 previousStreamPosition을 사용하여 기본 스트림을 폴링 할 때의 마지막 위치를 기억합니다. 또한 이미 전송 된 바이트 수와 스트림의 총 길이를 기반으로 totalBytesTransferredShouldBe으로 간단한 계산을 수행합니다.

마지막으로, 타이머가 생성되며 그것은 TotalBytesTransferred를 증가해야하는지 검사마다 눈금시되는 실행된다. 더 이상 업데이트를하지 않아도된다면 (기본적으로 스트림의 끝에 도달 함) 타이머를 중지하고 삭제합니다.

이 모두가 나에게 아주 작은 얻을 수 있습니다 더 정확하게 파일 전송이 달성 속도를 측정 나를 더 나은,보다 유연한 방법으로 전체 진행 상황을 계산할 수있는 전송되어 얼마나 많은 바이트 읽습니다.