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.");

上記では、基本的に使用しているチャンクサイズに基づいてバッファに書き込みます(この場合、2MBであり、チャンクサイズが大きいまたは小さい場合に比べて転送速度が最高であることがわかりました)。次に、中断した場所を覚えておくために少し作業を行い、バッファを返します。

次のコードはサーバー側の作業です。

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

WCF呼び出しでクライアントによって呼び出されるこのメソッドでは、次のチャンクの情報を取得し、メッセージを作成し、タイマーを少し使用して、転送が完了したらセッションを停止し、転送速度を更新します。メッセージを返す直前に、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このクラスの外に、内部で更新されるため、のプロパティをポーリングする別のクラスがあります。

投稿しすぎたコードをお詫びしますが、何を投稿すればいいのかわかりませんでした。

編集:少なくともサーバー側の実装では、転送されたバイト数をより詳細に読み取る方法は、ストリームの戻りメッセージ値の位置を読み取ることです。しかし、私のカウントで絶対的な整合性を確保するためにこれを行う方法がわかりません。ストリームが転送されているときにタイマーを使用して位置をポーリングすることを考えましたが、次の呼び出しが行われる可能性があり、すぐに同期がとれなくなりました。

戻ってきたストリームからデータをポーリングし、ストリームがいつ終了したかをすぐに知ることができるので、ストリームの残りの部分をバイトカウントにすばやく合計できますか?

4

1 に答える 1