题外话:这个系列的文章记录了本人最近写的一个小工程,主要包含了两个功能,一是对文件的断点续传的功能,二是基于WCF的一对多文件主动发送的功能,顺便这也是我自己在WCF学习路上的一个小成果吧。

在网上找了很多关于文件断点续传的例子,没看到有写的特别好的,可能好的代码都是内部使用的吧。因为自己在项目中时常会遇到文件传输的问题,所以这次就按照自己的想法来实现了一个,要说写的时候有没有参考过别人的代码,那肯定是没有的,因为我参考的是某c论坛里关于bt种子和迅雷文件下载的实现思路,虽然只有几句话而已,但也激发了我的灵感,于是就诞生了这个程序。

文章先不上源代码,想看干货的各位请直接翻到文章最底部查看我的Github。想要我的源代码吗?想要的话可以全部给你,去找吧!我把所有源码都放在那里!

正篇:

写这篇文章的目的有两个,一是作为学习日记,记录一下程序思想建立的过程,二是想对保存这个文件断点续传的功能,方便日后重复使用。

首先记录一下思路,一开始也会从网上先找找看有没有现成的关于文件传输的工具类,但方式大同小异,使用一个while循环,在循环内不停地读取文件,然后调用发送方法,直到文件读取完毕为止,示例代码见Fig.1。用这种直接的方式读文件发文件,是做不到断点续传的,那么问题来了,要怎么改才能让它支持断点续传呢?

Fig.1 普通的文件读取方式

我们可以从通信上下手,想要实现断点续传,不附带一些相关的文件信息是不可能的,最好形成消息闭环。简单的实现思路如Fig.2所示。为了方便接下来的说明,我写到一半又补画了个流程图…..,所以如果出现文笔不通顺的情况,那就是突然出现的流程图的锅。

Fig.2 双方协议交互流程

下面要讲的是发送端和接收端双方的交互协议,每次发送文件前,读取将要发送的文件信息FileInfo,包括文件名FileName、文件大小FileLength、文件的MD5之类的信息,然后再人为选取一个数值作为单个文件块大小BlockSize,将整个文件分成N个小文件块,根据文件大小计算出文件块的数量BlockCount、最后一个文件块的大小LastBlockSize,我们将文件块的信息称为FileBlockInfo。

PS:这里插一嘴,文件块大小的选取有一定的规则,若选的太小,文件块数量会较多,导致发送次数也会多,由于每次发送会附带除文件之外的相关信息,所以这将会额外的增加网络的负担;而若将文件块大小选的过大,则可能会超过单次网络发送数据长度的限制导致连帧或粘包的问题。这个大小应该根据具体使用的网络协议(HTTP、TCP等)来确定,最好测试一下。

有了这些发送时附带的信息还不够,还需要接收端的反馈,说了要形成闭环嘛。具体文件接收端除了将数据流写入文件之外,还需要做的事情,就应该在这个时候考虑了。为了实现断点续传,最重要的一步,是接收端需要给发送端反馈文件已经写到哪个位置了,也就是【断点】Writing Offset,发送端再根据这个【断点】指向的位置,调整接下来要发送的数据即可。等到所有数据都发送完毕后,再进行一次整体的文件MD5哈希校验,保证文件在传输后也是准确的,如果校验失败的话,就要考虑重新发送文件。

大体的实现思路可以说就是这样了,具体实现的时候还会遇到诸如“如何判断文件断点的位置”、“大文件校验失败后如何处理”、“在一个接收函数里写的判断分支太多了怎么维护”,这些问题我会在下面进行介绍。

问题一:如何判断文件断点的位置?

既然提到了断点,那么前置条件一是:该文件已存在,若文件不存在,断点续传无从说起;前置条件二是:该文件的长度小于发送端的文件长度,说白了就是这个文件处于传了一部分但是没传完的状态。

有一种简单暴力的思路,就是直接回传该文件当前的长度,让发送端接着后面发送数据就可以了。我觉得这种思路有一定的可行性,但缺点是,一旦出现网络波动导致数据断帧,而发送端误以为已经发送完整,就会导致后面接收的数据全都错位了。所以,回传的不是已存在文件的长度,而是根据已存在文件的长度和FileBlockInfo计算得出当前最后一个文件块的位置,让发送端从该位置发送,这样就保证了我后续文件块的完整性,如Fig.3。

Fig.3 断点位置计算

问题二:大文件校验失败后如何处理?

一般来说,当获取到数据发送失败时,应当再发送一次数据。但考虑到大文件的校验失败,可能仅因为小部分文件块没有正确写入而导致的,那直接重传不仅耗流量,而且也很耗时间。所以针对这种情况,我才在要在发送时计算各个文件块的MD5值,为的就是在整体文件的MD5检查失败后,对各个文件块进行块的MD5对比,这样可以不用把所有文件块都重新传输一次,可以节约不少时间。虽然这样牺牲了发送的效率,但可以使用多线程的方法缩短计算BlockMD5时产生的空档。

问题三:数据接收事件里写的判断分支又那么多,怎么维护?

文章前面说了不少“遇到某某情况该怎么怎么处理”,即每一种情况就要写一个分支,而在分支中的处理过程又长,全都写在接收函数里会让这块代码难以维护。于是我想到了使用设计模式中的状态模式(状态机),将具体处理的部分局部化,将不同状态的行为分割开来,而且,各个状态之间还可以灵活地相互转化。关于Fig.4这种设计模式,不了解它的童鞋可以在网上搜一搜,我就不重复介绍啦。

Fig.4 状态模式UML图

Fig.5 文件数据接收状态模式的实现

以下是完整工程文件,使用VS2017编译,已经在我的个人云服务器上测试过了,可以跨局域网传输。

https://github.com/wingsziye/RemoteWcfFileTransfer

Fig.6 一些效果如图

2018年12月21日星期五

基于WCF的支持跨局域网可断点续传的大文件传输服务实现的更多相关文章

  1. WCF大文件传输服务

    由于项目需要,自己写一个基于WCF的大文件传输服务雏形.觉得有一定的参考价值,因此放在网上分享. 目前版本为v1.1特点如下: 1.文件传输端口为18650 2.上传和下载文件 3.支持获取文件传输状 ...

  2. 转:wcf大文件传输解决之道(2)

    此篇文章主要是基于http协议应用于大文件传输中的应用,现在我们先解析下wcf中编码器的定义,编码器实现了类的编码,并负责将Message内存中消息转变为网络发送的字节流或者字节缓冲区(对于发送方而言 ...

  3. 转:wcf大文件传输解决之道(1)

    首先声明,文章思路源于MSDN中徐长龙老师的课程整理,加上自己的一些心得体会,先总结如下: 在应对与大文件传输的情况下,因为wcf默认采用的是缓存加载对象,也就是说将文件包一次性接受至缓存中,然后生成 ...

  4. WCF大文件传输【转】

    http://www.cnblogs.com/happygx/archive/2013/10/29/3393973.html WCF大文件传输 WCF传输文件的时候可以设置每次文件的传输大小,如果是小 ...

  5. 在ASP.NET中支持断点续传下载大文件(ZT)

    IE的自带下载功能中没有断点续传功能,要实现断点续传功能,需要用到HTTP协议中鲜为人知的几个响应头和请求头. 一. 两个必要响应头Accept-Ranges.ETag         客户端每次提交 ...

  6. WCF大文件传输

    WCF传输文件的时候可以设置每次文件的传输大小,如果是小文件的时候,可以很方便的将文件传递到服务端,但是如果文件比较大的话,就不可取了 遇到大文件的话可以采取分段传输的方式进行文件传输 思路: 1.客 ...

  7. .net大文件传输断点续传源码

    IE的自带下载功能中没有断点续传功能,要实现断点续传功能,需要用到HTTP协议中鲜为人知的几个响应头和请求头. 一. 两个必要响应头Accept-Ranges.ETag 客户端每次提交下载请求时,服务 ...

  8. 基于Web和二维码的文件传输服务

    在工作中难免需要对外提供一些我们抓取的log或者操作视频之类的资料,但由于工作环境日渐规范和严格,公司的网络环境和客户的网络环境是被独立开来的.这样做的好处不必多说,但同时也给我们工作带来的诸多不便. ...

  9. 基于TCP协议的大文件传输(粘包问题处理)

    基于TCP的大文件上传服务端实现 # 服务端 # -*- coding: utf-8 -*- from socket import * import json, struct server = soc ...

随机推荐

  1. sql,求和小于一定值的数据行

    select count(id),sum(Price) from [T_AddPrice] as a --order by id

  2. catkin_make编译错误

    在工作空间下编译ros的代码包,报错如下, CMake Error at LeGO-LOAM/LeGO-LOAM/CMakeLists.txt: (find_package): By not prov ...

  3. 【mysql】字段类型和长度的解释

    int(11)最大长度是多少,MySQL中varchar最大长度是多少? int(11)最大长度是多少? 在SQL语句中int代表你要创建字段的类型,int代表整型,11代表字段的长度. 这个11代表 ...

  4. ES - es为什么要移除type?

    1.index.type的初衷 之前es将index.type类比于关系型数据库(例如mysql)中database.table,这么考虑的目的是“方便管理数据之间的关系”. 2.为什么现在要移除ty ...

  5. PHP 概览

    运行环境 Windows Linux  Centos Ubuntu MacOS 编辑器 编码风格 依赖管理 Composer 发布包 热门框架 热门类库 热门系统 架构支持 源码分析 扩展开发

  6. OpenResty的现状、趋势、使用及学习方法

    Nginx 是俄罗斯人发明的, Lua 是巴西几个教授发明的,中国人章亦春把 LuaJIT VM 嵌入到 Nginx 中,实现了 OpenResty 这个高性能服务端解决方案. 通过 OpenRest ...

  7. 学习笔记《Java多线程编程实战指南》四

    JAVA线程同步机制 线程同步机制:是一套用于协调线程间的数据访问及活动的机制,该机制用于保障线程安全以及实现这些线程的共同目标.java平台提供的线程同步机制包括锁.volatile关键字.fina ...

  8. Linux基础之常用命令整理(一)

    Linux 操作系统的安装 如今比较流线的linux操作系统 Centos Redhat  Fedora Ubuntu, 安装操作系统的提前是要有操作系统的镜像文件(.iso文件)并且必须为系统指定一 ...

  9. KeyPress和KeyDown/KeyUp

    (1) KeyPress 和KeyDown .KeyUp之间的区别:    1).KeyPress主要用来捕获数字(注意:包括Shift+数字的符号).字母(注意:包括大小写).小键盘等除了F1-12 ...

  10. 初识git(17/8/21)

    git是一个分布式的版本管理系统 通过廖雪峰的官方网站(maybe2017)来学习的,比较详实跟着操作就行,记录基本的一些命令还有学习是遇到的一些问题和收获,方便下次查阅. git的安装 -. win ...