传输文件
定义
TouchSocket.Dmtp.FileTransfer <br/>dotnet add package TouchSocket.Core一、说明
文件传输是每个框架都需要的功能,也是检验一个框架性能的非常重要的指标。
本组件则是基于Dmtp,开辟了全双工对点文件传输。即,当客户端连接服务器以后,客户端可以向服务器请求、推送文件,服务器也能向客户端请求,推送文件。甚至,客户端之间,也可以互相请求,推送文件。
其特点包括:
- 全双工对点文件传输。即:客户端、服务器、其他客户端三者之间,可以互相推送、请求文件。
- 高性能、低GC。整个传输过程,将内存池用到极致,极大的减少不必要的GC。本地电脑实测传输速度达到1.2Gb/秒。
- 全平台支持。Windows、Android、Unity3D(除webgl)全部支持。
- 支持任意大小的文件传输(实测100Gb没有问题)。
- 支持断点续传。
- 支持传输限速。
- 支持文件多链路、多线程传输 。
二、支持插件
| 插件方法 | 功能 |
|---|---|
| IDmtpFileTransferringPlugin | 文件传输之前触发。仅在响应端有效。 |
| IDmtpFileTransferredPlugin | 文件传输结束后可能触发,触发时不代表传输成功,具体状态查看e.Result属性。 仅在响应端有效。 |
三、性能
可以看到,下图正在上传一个Window的系统镜像文件,大约4.2Gb,传输速度已达到800Mb/s,GC基本上没有释放,性能非常强悍(中间有稍微停顿,因为程序在获取文件MD5值)。
四、产品应用场景
五、传输流程及名词介绍
5.1 响应流程
- 请求端(可能是客户端,也可能是服务器)调用Pull(请求)或Push(推送)。
- 响应方(可能是服务器,也可能是客户端)触发FileTransferring。
- 返回文件信息,然后检验是否续传等,然后开始传输。
- 传输完成或异常。
- 响应方可能触发FileTransferred。
- 请求端函数返回,
FileOperator控制器状态改变。
响应方必须在订阅的OnFileTransferring函数中,同意每一个传输(e.IsPermitOperation = true),不然会直接拒绝请求。当然如果是有意拒绝,则可以通过e.Message返回拒绝的信息。
响应方订阅的OnFileTransferred事件的触发并不意味着完成传输,具体结果还要通过Result属性值进行判断。
请求端返回的result,在成功时,可以100%表明传输的文件已在磁盘上。如果想进一步确定文件的准确性,还需要自行再验证MD5或者其他Hash算法。
5.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。
声明MyPlugin插件,是为了方便处理OnDmtpFileTransferring和OnDmtpFileTransferred函数。
6.1 客户端向服务器请求文件
【客户端代码】
6.2 客户端向服务器推送文件
6.3 服务器向客户端请求文件
服务器主动向客户端请求文件,必须通过Id,找到其SessionClient的派生类。
6.4 服务器向客户端推送文件
服务器主动向客户端推送文件,必须通过Id,找到其SessionClient的派生类。
6.5 客户端之间传输文件
该功能支持客户端之间传输文件,使用方法基本一致,只需要在请求PullFile或者PushFile时额外增加目标Id即可。
此外,**响应中介(一般是服务器)**需要添加路由策略和同意路由。
【添加路由策略】
【同意路由】
如果不添加DmtpRouteService策略,将不会转发任何路由请求,即意味着请求会被直接响应。
其他PullFile或者PushFile操作均和上述一致。
七、断点续传
本文件传输,支持断点续传。能够在传输过程中,暂停(实际上就是取消)传输,下次继续传输。
断点续传的使用步骤如下:
- 当本次传输失败、或主动取消时,可以保存
fileOperator.ResourceInfo对象属性。 - 当下次再次请求传输时,可以将已保存的
ResourceInfo对象,先赋值给fileOperator.ResourceInfo属性。那么如果保存路径一致,且文件大小符合续传要求,则可以继续传输。
本断点续传不验证文件唯一性,假如在续传前后文件大小不一致,则会导致续传失败。如果文件大小一致,但是内容已发送变化,则可能得到一个错误的文件。所以建议在续传时要么确定文件不会再变化,要么需要在传输完成后进行文件唯一性验证(例如MD5)。
文件续传适用于所有传输类型,无论是客户端对服务器,还是服务器对客户端,还是客户端对客户端。
一般来说,续传信息是不需要持久化的。只需要使用一个变量(或字典存一下即可)。但是如果考虑进程退出,或程序崩溃等情况,则需要持久化续传信息。
八、取消传输
传输取消,指的是,在传输过程中,用户主动取消传输。
8.1 Token传递
使用方法非常简单,因为FileOperator中允许传入一个可取消令箭。所以只需要通过CancellationTokenSource,将Token传入,然后直接使用CancellationTokenSource取消即可。
8.2 使用CancellationFileOperator
CancellationFileOperator是继承FileOperator并且已经实现取消传输的操作器。原理和Token一致,这里只是做了一次封装。
取消传输和断点续传结合,就能模拟暂停传输功能。这样,用户就可以在传输过程中,随时暂停和继续传输。并且没有暂停时长限制。
九、小文件传输
小文件传输是指,当传输文件小于设定大小(默认1024*1024字节)时的传输。
为什么要设立小文件传输?与常规文件传输相比,优点在哪里?
常规传输,建立一个传输通道,大约需要传输两端,往返通信4-6次。这在本地局域网中,显得无所谓。但是在互联网环境中,一次ping延迟平均50ms,那么建立一个传输,就大约需要200-300ms。这也就意味着,即使一个文件只有一字节,也需要200ms-300。所以,这明显是不合理的。所以又新增了小文件传输,只要文件在1Mb以内,仅往返1次,就可以完成传输。
9.1 拉取小文件
- 直接调用PullSmallFile或者PullSmallFileAsync,获取到实际的文件数据。
- 通过Save方法,将数据写入文件。也可以自行保存。
9.2 推送小文件
【推送文件】
- 直接调用PushSmallFile或者PushSmallFileAsync。
- 返回值即表示是否成功。
小文件传输也支持服务器主推至客户端,和客户端之间相互传输。
十、多线程文件传输
多线程文件传输,顾名思义,就是多个连接链路,共同传输一个文件。
多线程传输的优点是什么?和常规文件传输相比,场景有哪些不同?
首先,常规文件传输是基于单个连接链路的,所以,单个连接的传输速率上限,就是常规传输的上限。一般来说,局域网当中,单个连接即可占满所有带宽,所以这时候多线程传输和常规传输并无差别。但是,在云服务器,或者在有流量均衡算法的网络中,每个连接的最大速率不是带宽的最大速率,那么这时候,两个差距是比较大的。
例如,我自己租的一个单核云服务器,它的单个连接速率只有1Mb,但是弹性带宽却有10Mb。宏观表象就是,一个客户端连接时,可以用1Mb带宽,两个客户端连接时,就可以用2Mb。那么这时候,多线程传输就显得格外重要了。
10.1 创建多链路连接器
因为是多链路传输,所以,就必须建立多个客户端的连接到服务器。这里使用已经封装好的通信模型ClientFactory。
ClientFactory的通信模型使用的是一个主通信端+多个传输客户端。
对于客户端的配置,请详细参考创建DmtpClient工厂
10.2 多线程请求文件
10.3 多线程推送文件
10.4 客户端之间传输文件
该功能也支持客户端之间互相传输。使用方法基本一致,需要额外指定目标Id,以及获取传输的Id集合即可。
多线程的客户端之间传输文件,不像其他操作类型那么简单。因为除了需要指定目的Id外,还需要指定获取目标Id的,传输客户端的Id集合,不然,获取数据的时候,仍然会是单线程工作的。
【获取目标传输客户端的Id集合】 在TcpDmtpClientFactory属性中,有个OnFindTransferIds。通过实现该属性,使其能够获取到对应客户端的传输客户端Id集合(下列代码为模拟值,要具体实现该功能,还得自行实现)。
多线程传输,目前暂不支持服务器主动向客户端传输。