BT源代码学习心得(十一):客户端源代码分析(存储管理)

BT源代码学习心得(十一):客户端源代码分析(存储管理)
Author:wolfenstein
 
这一次分析BT的存储管理。我们知道,BT把要共享的资源化分成统一大小的块,并且在种子文件中记录每一块的消息摘要值,以便在下载时确定某一块是否已经正确下载。而且在前面的种子文件的制作过程中我们已经看到,除非是最后一块,其它的块大小都是相同的,因此很有可能出现在一个文件的开始多少个字节属于某一块,然后从中间偏移多少字节又属于某一块,或者在文件比较小的情况下某一块包含了若干文件等。而BT的存储管理部分就对程序的其它部分屏蔽了这些区别,即对其它部分而言,只需要按照块来进行存取。
首先了看FilePool类,它在Multitorrent中定义,就是说,全局只有一个。因此它可以保证多个种子文件在下载时硬盘上的文件被打开的数量限制在一定数量。内部维护了如下变量:handlebuffer为所有已经打开的文件的列表,allfiles是一个字典,记录所有的文件的拥有者情况,即哪个文件是属于哪个种子文件。它的关键字是各个文件的文件名,值则是对应的_SingleTorrent对象。handles则是记录文件名与对应句柄关系的字典,whandles还说明了哪些文件是可写的,注意,在whandles中,并没有储存对应句柄,即如果有一个文件出现在handles中,可以通过handles直接获取其句柄,避免多次打开或者关闭文件,而如果它出现在whandles中,那么说明它还是可写的。
Storage是在每个_SingleTorrent中被定义。创建Storage需要一个文件和它们对应的大小的列表,文件的大小方面的信息可以从种子文件的元信息里得到,另外,必须要把种子文件的元信息的文件列表中的每一个项目都加上在硬盘中实际保存的目录,以便可以直接对应到某个具体的文件。Storage在创建的时候建立文件名和全局的字节之间的映射关系,即列表ranges,该列表的每一个列表项是一个三元组,起始偏移,结束偏移,文件名。它的意思是种子文件的内容中,从第几个字节到第几个字节是属于哪个文件。另外,假设种子文件中对所有信息的内容进行了块的划分,设这个块长为piecelen,那么每一块还有一个字节偏移,如从第0个字节到第piecelen(不含)个字节是属于第一块,接下来属于第二块等。因此这个Storage类就要解决BT的下载按块进行和硬盘中按文件进行存储之间的矛盾。这里再提一下,之所以把piece翻译成块,是因为后面的BT的下载过程中,还要把每一块再切分成若干的slice,而我习惯于把slice翻译成片。
Storage中,有两个私有函数_intervals_get_file_handle,它们给readwrite提供了两项重要的功能,而readwriteStorage对外提供的重要接口。_intervals的任务是提供一个全局的偏移量和长度,返回一张表,说明要对这些数据进行访问应该分别访问哪些文件的第几个字节开始的多少字节。这样,在readwrite里面就可以用for...in_intervals(xx,xx)了。而_get_file_handle则是获取一个文件的实际句柄,以便对其进行读写,在Storage中获取文件的句柄要用一个函数来处理的原因是必须考虑到文件打开数量的要求限制,因而在_get_file_handle中要和FilePool打交道,在打开一个文件句柄的同时,要对FilePool内部的一些变量进行维护。
Storage还有一项功能就是读写一个快速恢复状态文件。它只负责读写一部分,这个文件中还有一部分数据由StorageWrapper类来进行读写。由Storage类负责读写的部分是文件头,包括'BitTorrentresumestatefile,version1'字样,和总的数据量,以及各个文件的大小和更改时间等。
StorageWrapper类则是在_SingleTorrent._start_download中定义,提供的接口要更加高级。例如,它提供按照某一块来进行访问,然后在内部通过把块号乘以一块的大小来得到实际的字节偏移量,然后让Storage来进行读写。另外,它维护了一张本地拥有哪些块的比特数组have,可以方便决策。还有两个表示块的存储状况的数组,placesrplaces,它们的意思是数据的第几块存储在硬盘上的第几块以及硬盘上的第几块存储的实际上是数据的第几块。这个数组基于两部分数据的抽象:第一部分的数据抽象是把种子文件中所表示的内容(即共享的资源)看成是一块连续的数据,这块数据有若干块。第二部分的数据抽象是把存储在硬盘上的文件,看成是一块连续的数据,即连续的存储空间,也分成若干块。当下载任务全部结束后,应该有对于0self.numpieces-1,有places[i]=rplaces[i]=i。而在下载过程中,由于采取了一定的策略,不一定是先下第0块,再下第1块等,因此在这个过程中有可能places[i]rplaces[i]中的值不等于i
我们先来看一下比特数组,即Bitfield,它定义在BitTorrent/bitfield.py中。它用最节约的空间完成了比特数组的存储,即比特数除于8的字节数。另外,它实现了__setitem____getitem__,这样就可以直接对have[i]进行读写操作来完成值的操作。注意到在__setitem__的实现中有assertval,这就意味着只能把数组中的某一项赋值成1。这项功能比较适用于表示块拥有的状况,即某一块只能从没有到有,不能得而复失
StorageWrapper还有一项很重要的功能就是对每一块数据再次细分成若干个slice,而一个slice就是两个对等客户之间通过网络进行数据交换的最小单位。在此基础上,它要负责生成请求,inactive_requests储存的就是所有可能的请求。在看这部分代码时,注意1l的区别,在初始化时,inactive_requests[i]的值是1,表示某一块还没有(因而可以为此生成网络请求),当得知某一块已经获取时,inactive_requests[i]的值变为None,具体得知某一块已经获取时要进行的操作为markgot,它的意义是第piece块在硬盘上的存储空间中的第pos块被发现。另外,初始化的时候调用_check_partial_make_partial检查某个具体的块,看看有哪些slice还需要下载。把这些请求放到inactive_requests中,以后当程序其它部分决定要开始下某一块时,StorageWrapper为其生成相应的网络请求的参数(第几块,偏移多少,请求多少长度的数据)new_request即完成这项工作,另外piece_came_inget_piece提供数据的读写操作,调用它们的时候都要指定index(第几块)begin(块内偏移)length(长度,在piece_came_in中是piece,即数据本身,可以直接得到它的长度)
最后,关于存储管理这部分,还有一点需要提到,那就是早期的某个BT版本是在下载刚刚开始的时候就申请好相应的硬盘空间。而现在则是随着下载的进行文件逐渐增大。但是下载的顺序很可能不是先下第0块,再下第1块这样,因此在文件中存储的顺序也不一样,这样当新的下载数据到来存储到硬盘上时,很可能就要对起进行调整,尽可能让它们对号入座_move_piece函数就能进行数据的移动,而参考piece_came_in开头部分对_move_piece调用的代码就可以理解BT在下载过程中逐渐使块的顺序对号入座的这个过程。
本项目是一个基于SSM(Spring+SpringMVC+MyBatis)框架和Vue.js前端技术的大学生第二课堂系统,旨在为大学生提供一个便捷、高效的学习和实践平台。项目包含了完整的数据库设计、后端Java代码实现以及前端Vue.js页面展示,适合计算机相关专业的毕设学生和需要进行项目实战练习的Java学习者。 在功能方面,系统主要实现了以下几个模块:用户管理、课程管理、活动管理、成绩管理和通知公告。用户管理模块支持学生和教师的注册、登录及权限管理;课程管理模块允许教师上传课程资料、设置课程时间,并由学生进行选课;活动管理模块提供了活动发布、报名和签到功能,鼓励学生参与课外实践活动;成绩管理模块则用于记录和查询学生的课程成绩和活动参与情况;通知公告模块则实时发布学校或班级的最新通知和公告。 技术实现上,后端采用SSM框架进行开发,Spring负责业务逻辑层,SpringMVC处理Web请求,MyBatis进行数据库操作,确保了系统的稳定性和扩展性。前端则使用Vue.js框架,结合Axios进行数据请求,实现了前后端分离,提升了用户体验和开发效率。 该项目不仅提供了完整的源代码和相关文档,还包括了详细的数据库设计文档和项目部署指南,为学习和实践提供了便利。对于基础较好的学习者,可以根据自己的需求在此基础上进行功能扩展和优化,进一步提升自己的技术水平和项目实战能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值