传输文件
定义
命名空间:TouchSocket.Dmtp.FileTransfer
程序集:TouchSocket.Dmtp.dll
程序集:TouchSocketPro.Dmtp.dll(多线程传输)
一、说明
文件传输是每个框架都需要的功能,也是检验一个框架性能的非常重要的指标。
本组件则是基于Dmtp,开辟了全双工对点文件传输。即,当客户端连接服务器以后,客户端可以向服务器请求、推送文件,服务器也能向客户端请求,推送文件。甚至,客户端之间,也可以互相请求,推送文件。
其特点包括:
- 全双工对点文件传输。即:客户端、服务器、其他客户端三者之间,可以互相推送、请求文件。
- 高性能、低GC。整个传输过程,将内存池用到极致,极大的减少不必要的GC。本地电脑实测传输速度达到1.2Gb/秒。
- 全平台支持。Windows、Android、Unity3D(除webgl)全部支持。
- 支持任意大小的文件传输(实测100Gb没有问题)。
- 支持断点续传。
- 支持传输限速。
- 支持文件多链路、多线程传输 。
二、性能
可以看到,下图正在上传一个Window的系统镜像文件,大约4.2Gb,传输速度已达到800Mb/s,GC基本上没有释放,性能非常强悍(中间有稍微停顿,因为程序在获取文件MD5值)。
三、产品应用场景
四、传输流程及名词介绍
4.1 响应流程
- 请求端(可能是客户端,也可能是服务器)调用Pull(请求)或Push(推送)。
- 响应方(可能是服务器,也可能是客户端)触发FileTransferring。
- 返回文件信息,然后检验是否续传等,然后开始传输。
- 传输完成或异常。
- 响应方可能触发FileTransferred。
- 请求端函数返回,
FileOperator
控制器状态改变。
响应方必须在订阅的OnFileTransferring函数中,同意每一个传输(e.IsPermitOperation = true),不然会直接拒绝请求。当然如果是有意拒绝,则可以通过e.Message返回拒绝的信息。
响应方订阅的OnFileTransferred事件的触发并不意味着完成传输,具体结果还要通过Result属性值进行判断。
请求端
返回的result,在成功时,可以100%表明传输的文件已在磁盘上。如果想进一步确定文件的准确性,还需要自行再验证MD5或者其他Hash算法。
4.2 传输控制器
FileOperator
是本次文件传输的操作器,主要用于获取传输进度、速度、状态以及取消传输等操作。
可配置参数:
(1)ResourcePath
资源路径,在上传时,表示发起端的文件路径。在下载时,表示请求的文件路径。当该值为相对路径时,会与响应对点的RootPath组合路径。当为绝对路径时,则会直接访问路径文件。
如果是下载行为,响应方可在在订阅的OnFileTransferring函数中,随意重定向请求的文件路径(e.ResourcePath)。
当以绝对路径访问时,对方可能会请求到服务器电脑的所有文件,所以最好在OnFileTransferring里面进行安全的判断后再放行(e.IsPermitOperation = true;)。
(2)SavePath
保存路径,在上传时,表示需要保存的文件路径。在下载时,表示本地保存的文件路径。同样,当该值为相对路径时,会与接收对点的RootPath组合路径。当为绝对路径时,则会直接生效。
如果是上传行为,响应方可在在订阅的OnFileTransferring函数中,随意重定向文件的保存路径(e.SavePath)。
(3)ResourceInfo
已存在的资源信息,当上个传输失败时,可以保存其ResourceInfo,然后重新传输时赋值,即可尝试断点续传。
当执行续传时,本次传输与前次传输间隔时间不应该超过响应默认值(60秒)。
(4)CompletedLength
已完成流长度。
(5)Speed 函数
从上次获取到此次获得的速度。一般请每秒钟调用一次获取速度值。
当获取传输速度时,其值和获取时间完全相关。例如:假如实际每秒传输速度为100,当每隔一秒获取时,则为100.当每隔100毫秒获取时,则为10。
(6)Progress
传输进度,范围0-1。
(7) Result
获取传输状态以及状态信息。当ResultCode为Default时,意味着传输正在进行。
(8) Token
CancellationToken类型的可取消令箭。
(9) Metadata
string类型的键值对,用于和接收方交 互数据。
五、 传输文件
传输文件功能,是DmtpFileTransferFeature
功能插件基于Dmtp协议提供的功能。所以,Dmtp服务器
和客户端
都必须配置添加该功能插件。即调用UseDmtpFileTransfer
。
config.ConfigurePlugins(a =>
{
a.UseDmtpFileTransfer()// 必须添加文件传输插件
//.SetRootPath("C:\\新建文件夹")//设置RootPath
.SetMaxSmallFileLength(1024 * 1024);//设置小文件的最大限制长度
a.Add<MyPlugin>();
})
声明MyPlugin
插件,是为了方便处理OnDmtpFileTransferring
和OnDmtpFileTransferred
函数。
internal class MyPlugin : PluginBase, IDmtpFileTransferringPlugin, IDmtpFileTransferredPlugin
{
private readonly ILog m_logger;
public MyPlugin(ILog logger)
{
this.m_logger = logger;
}
/// <summary>
/// 该方法,会在每个文件被请求(推送)结束时触发。传输不一定成功,具体信息需要从e.Result判断状态。
/// 其次,该方法也不一定会被执行,例如:在传输过程中,直接断网,则该方法将不会执行。
/// </summary>
/// <param name="client"></param>
/// <param name="e"></param>
/// <returns></returns>
public async Task OnDmtpFileTransferred(IDmtpActorObject client, FileTransferredEventArgs e)
{
//传输结束,但是不一定成功,甚至该方法都不一定会被触发,具体信息需要从e.Result判断状态。
this.m_logger.Info($"传输文件结束,请求类型={e.TransferType},文件名={e.ResourcePath},请求状态={e.Result}");
await e.InvokeNext();
}
/// <summary>
/// 该方法,会在每个文件被请求(推送)时第一时间触发。
/// 当请求文件时,可以重新指定请求的文件路径,即对e.ResourcePath直接赋值。
/// 当推送文件时,可以重新指定保存文件路径,即对e.SavePath直接赋值。
///
/// 注意:当文件夹不存在时,需要手动创建。
/// </summary>
/// <param name="client"></param>
/// <param name="e"></param>
/// <returns></returns>
public async Task OnDmtpFileTransferring(IDmtpActorObject client, FileTransferringEventArgs e)
{
foreach (var item in e.Metadata.Keys)
{
Console.WriteLine($"Key={item},Value={e.Metadata[item]}");
}
e.IsPermitOperation = true;//每次传输都需要设置true,表示允许传输
//有可能是上传,也有可能是下载
this.m_logger.Info($"请求传输文件,请求类型={e.TransferType},请求文件名={e.ResourcePath}");
await e.InvokeNext();
}
}