Java断点续传多线程下载器(1)-原理和构想

  • 刚刚学习了Java多线程基础和计算机网络等等,没个实践不得行,尝试着写一个Java多线程下载器来玩玩

我说封面是我老婆,没人有意见吧?


前期的原理学习和项目构想

基础概念的疏导
第一眼看到断点续传多线程下载器时候发出的疑问?

这玩意第一眼看上去会出现很多问题也很正常嘛,绝对不只是因为我菜啊。我们平时最常用的下载器应该就是迅雷了,或者就是在浏览器上直接下载。如果让我自己来搞一个下载器的话,我的第一反应可能是个传输工具的demo吧。于是,出现了以下的问题:

  • 断点续传是啥?

  • 多线程下载器是什么意思?

  • 多线程下载和单线程下载比有啥更屌的吗?

  1. 断点续传是啥

    • FTP(文件传输协议的简称)(File Transfer Protocol、 FTP)客户端软件断点续传指的是在下载或[上传](https://baike.baidu.com/item/上传/749624)时,将下载或上传任务(一个文件或一个[压缩包](https://baike.baidu.com/item/压缩包/5373066))人为的划分为几个部分,每一个部分采用一个[线程](https://baike.baidu.com/item/线程/103101)进行上传或下载,如果碰到[网络故障](https://baike.baidu.com/item/网络故障/1391028),可以从已经上传或下载的部分开始继续上传下载未完成的部分,而没有必要从头开始上传[下载](https://baike.baidu.com/item/下载/2270927)。用户可以节省时间,提高速度。

      ​ — 一段百度百科送给大家

    • 说白了,就是我让你停你就停,下次让你继续,你就得哪里跌倒哪里爬起来。这个应该是一个下载器最基本的素质

  2. 多线程下载器是什么意思?

    • 从我们直观的角度来看,我们在下载的时候会有一个进度条,我们可能会以为只有一个通道在下载文件,但其实可能不是,线程和进程的概念就不在这里做赘述了。在下载的时候,下载器会把一个文件分割成很多份,每一份交由一个线程来下载,这样子是不是感觉到会变快很多呢?
    • 所以,假如我开了N个线程,是不是就比单线程要快N倍呢?那岂不是线程越多越好?事实上,线程并不是越多越好,N个线程也不是比单个线程要快N倍。因为,从根本上来看,线程同步也不过是宏观上看,从CPU的角度来看,无非是多个线程在CPU规划好的时间片内轮流占有CPU资源。并不是真正意义上的齐头并进。那么,就牵扯出了下一个问题:
  3. 多线程下载和单线程下载比有啥更屌的吗?

    • 多线程屌还是屌的,至少学长是这样说的,那我不管,我只管研究它屌在哪里。既然我们说,多线程快,并不是快在它真的可以齐头并进,那么它快在哪里呢? 这里就要牵扯出一个知识点了=>拥塞避免算法

      拥塞避免算法:

      ​ 哈?关于这个知识点,我做了笔记的,湖科大教书匠永远的神。

      拥塞避免算法的链接:juejin.cn/post/694580…

    • 所以,单线程如果被拥塞避免算法卡了一刀,整个下载速度就真的被砍了一刀。鸡蛋不要放在同一个篮子里,如果我们开了多线程,只要不是所有进程都被砍了一刀,就一定比单线程被砍一刀要快。

    • 当然,如果在整个下载过程根本没有发送IO阻塞,一点点都没有,那我觉得还是单线程更快。毕竟多线程的切换上下文会影响效率。

断点续传的实现原理

那行,我们了解了断点续传似乎是真的屌,就应该来思考如何来实现多线程断点续传了。因此,现在也遇到了新的疑问,:

  • 我怎么知道这个下载链接它给不给我断点续传?

  • 我怎么把一个文件待传部分给切成多份给多线程?

  • 如果我传了一半,过段时间源文件它更新了,怎么搞?

  • 我知道可以断点续传了,也知道了文件有没有更新了,然后用多进程下载下来了,然后呢?


  1. 我怎么知道这个下载链接它给不给我断点续传?

    • 首先,咱要了解一下HTTP协议,HTTP协议从1.1开始支持获取文件的部分内容,这为并行下载以及断点续传提供了技术支持。
    • 在这个HTTP请求中有请求头Request header 和 响应头Response header 。如果响应头中有Accept-Ranges: bytes //是否允许指定传输范围,bytes:范围请求的单位是 bytes(字节),none:不支持任何范围请求单位这个字段,那好,你可以断点续传。哪里趴下,哪里站起来,不然的话,从头来过吧,和打游戏档被人删了一样。
  2. 我怎么把一个文件待传部分给切成多份给多线程?

    • 我们可以对Content-Range字段进行设置,Content-Range:A-B 就是从第A个bytes开始下载到第B个bytes
  3. 如果我传了一半,过段时间源文件它更新了,怎么搞?

    • 对于这个问题,我们可以有两个关键字:ETagLast-Modified.
    • Last-Modified可以记录该文件最后更新的时间戳,以此来判断文件是否有做过修改,有的文件可能会周期性的修改,有的文件可能修改得贼拉快,有的服务器可能时间记录的不准,所以该字段似乎并不能够完全证明文件内容被修改过。
    • ETag是一个和文件相关的标记,可以是一个版本标记,可以理解成文件的指纹,但是 HTTP/1.1 标准并没有规定 Etag 的内容是什么或者说要怎么实现,唯一规定的是 Etag 需要放在 “ ” 内。
  4. 我知道可以断点续传了,也知道了文件有没有更新了,然后用多进程下载下来了,然后呢?

    • 然后就可以使用 RandomAccessFile 把下载的临时文件合并成一个文件了呀,然后这个项目的第一个版本就出现了呀

    只研究了基本流程,代码一个字没敲呢。有没有大佬带带我 ——2021.4.4