好的,我正在开发我的文件传输服务,我可以使用 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();
}
在这里,myTotalBytesTransferred
也是基于传入的块的长度。我知道如果我自己编写流而不是使用 for 流,我可以获得更精细的计算CopyTo
,但我不确定如何最好地进行对这个。
有人可以帮我吗?在这个类之外,我有另一个类在TransferSpeed
内部更新时轮询它的属性。
如果我发布了太多代码,我深表歉意,但我不确定该发布什么,不发布什么。
编辑:我意识到至少在服务器端实现中,我可以通过读取流的返回消息值的位置来更精细地读取已传输的字节数。但是,我不知道如何做到这一点,以确保我计数的绝对完整性。我考虑过可能使用计时器并在传输流时轮询位置,但随后可能会进行下一次调用,我很快就会变得不同步。
如何从返回的流中轮询数据并立即知道流何时完成,以便我可以快速将流中剩余的内容加到我的字节数中?