`
ruilinruirui
  • 浏览: 1048872 次
文章分类
社区版块
存档分类
最新评论

BT客户端源码分析之二:Storage 类

 
阅读更多

转载自:http://blueidea.bokee.com/1497128.html

作者:小马哥

日期:2004-6-28

由于 Storage 类比较简单,我直接在源码基础上进行注释。掌握Storage,为进一步分析 StorageWrapper 类打下基础。

几点说明:

1、 Storage 类封装了对磁盘文件的读和写的操作。

2、 BT既支持单个文件的下载,也支持多个文件,包括可以有子目录。但是它并不是以文件为单位进行下载和上传的,而是以“文件片断”为单位。这可以在BT协议规范以及另一篇讲BT技术的文章中看到。所以,对于多个文件的情况,它也是当作一个拼接起来的“大文件”来处理的。例如,有文件 aaabbb,大小分别是 4001000,那么它看作一个大小为 1400 的大文件,并以此来进行片断划分。

3、 文件在下载过程中,同时提供上传,所以是以读写方式打开的,wb+rb+都指的读写方式。在下载完毕之后,改为只读方式。

4、 由于下载可能中断,所以在 Storage 初始化的时候,磁盘上可能已经存在文件的部分数据,必须检查一下文件的大小。为了便于描述,我们把完整文件的大小称为“实际长度”,把文件当前的大小成为“当前长度”。

class Storage:

# files 是一个二元组的列表(list),二元组包含了文件名称和长度,例如:

[(“aaa”, 100), (“bbb”, 200)]

def __init__(self, files, open, exists, getsize):

self.ranges = []

# 注意,这里是 0l,后面的l表示类型是长整形,而不是 01

total = 0l

so_far = 0l

for file, length in files:

if length != 0:

# ranges 是一个三元组列表,三元组的格式是: 在“整个”文件的起始位置、结束位置、文件名。BT在处理多个文件的时候,是把它们看作一个拼接起来的大文件。

self.ranges.append((total, total + length, file))

total += length

# so_far 是实际存在的文件的总长度,好像没有起作用

if exists(file):

l = getsize(file)

if l > length:

l = length

so_far += l

# 如果文件长度为0 则创建一个空文件

elif not exists(file):

open(file, 'wb').close()

# begins 是一个列表,用来保存每个文件的起始位置

self.begins = [i[0] for i in self.ranges]

self.total_length = total

self.handles = {}

self.whandles = {}

self.tops = {}

# 对于每一个文件,,,

for file, length in files:

# 如果文件已经存在

if exists(file):

l = getsize(file)

# 如果文件长度不一致,说明还没有下载完全,则以读写(rb+)的方式打开文件。

if l != length:

handles 是一个字典,用来保存所有被打开文件(无论是只读还是读写)的句柄

whandles 是一个字典,用来记录对应文件是否是以写的方式打开(读写也是一种写)。

self.handles[file] = open(file, 'rb+')

self.whandles[file] = 1 (这里是数字1,而不是字母 l

#如果文件长度大于实际长度,那么应该是出错了,截断它。

if l > length:

self.handles[file].truncate(length)

如果文件长度和实际长度一致,那么下载已经完成,以只读方式打开。

else:

self.handles[file] = open(file, 'rb')

# tops 是一个 字典,保存对应文件的“当前长度”。

self.tops[file] = l (这里是字母 l,不是数字 1

# 如果文件并不存在,那么以读写(w+)的方式打开

else:

self.handles[file] = open(file, 'wb+')

self.whandles[file] = 1

# 判断起始位置为 pos,长度为 length的文件片断,在 Storage 初始化之前,是否就已经存在于磁盘上了。这个函数后面分析 StoageWrapper 类的时候会再提到。

如果已经存在,那么返回 true,否则为 false

注意:如果这个片断的部分数据已经存在于磁盘上的话,那么也返回 false

在分析 StorageWrapper 的时候,才发现这里分析的不对。这个函数意思应该是:

判断起始位置为 pos,长度为 length的文件片断,在 Storage 初始化之前,是否已经在磁盘上分配了空间。

例如,大小为 1024k的文件,如果获得了 1个片断(从256k512k),那么这时候,磁盘上文件的大小是 512k(也就是分配了512k),尽管第0个片断(从0256k)还没有获得,但磁盘上会保留这个“空洞”。

def was_preallocated(self, pos, length):

for file, begin, end in self._intervals(pos, length):

if self.tops.get(file, 0) < end:

return False

return True

# 将所有原来以 读写方式打开的文件,改成只读方式打开

def set_readonly(self):

# may raise IOError or OSError

for file in self.whandles.keys():

old = self.handles[file]

old.flush()

old.close()

self.handles[file] = open(file, 'rb')

# 获取所有文件的总长度

def get_total_length(self):

return self.total_length

这个函数意思是检查 起始位置 pos,大小为 amount 的片断实际位置在哪里?

例如,假设有两个文件,aaabbb,大小分别是 400 1000,那么 pos 300amount200的文件片断属于哪个文件了?它分别属于两个文件,所以返回的是

[(“aaa”, 300, 400), (“bbb”, 0, 100)]

也就是它既包含了 aaa 文件中从 300 400这段数据,也包含了 bbb 文件从 0 100 这段数据。

def _intervals(self, pos, amount):

r = []

# stop 是这个片断的结束位置。

stop = pos + amount

# 通过这个函数,可以首先定位在哪个文件中,注意,可能在多个文件中(如果某个文件过小,那么,一段数据可能跨越几个文件)

# 通过例子来解释下面这句,假设 begins = [ 100, 200, 400, 1000], pos = 250,那么 bisect_right(self.begins, pos) 返回的是 2,而p = bisect_right(self.begins, pos) – 1 就是 1,这表示起始位置为 250 的文件“片断”,它至少属于第1个文件(从 0 开始算起),也就是起始为200的文件。

p = bisect_right(self.begins, pos) – 1

# r 是一个三元组的列表,三元组格式是(文件名,在该文件的起始位置,在该文件的结束位置)。

while p < len(self.ranges) and self.ranges[p][0] < stop:

begin, end, file = self.ranges[p]

r.append((file, max(pos, begin) - begin, min(end, stop) - begin))

p += 1

return r

# 把从 pos开始,amount长的数据从文件中读出来,转换成一个字符串

def read(self, pos, amount):

r = []

for file, pos, end in self._intervals(pos, amount):

h = self.handles[file]

h.seek(pos)

r.append(h.read(end - pos))

# list 转换为一个字符串

return ''.join(r)

# 把一段字符串写到相应的磁盘文件中。

def write(self, pos, s):

# might raise an IOError

total = 0

for file, begin, end in self._intervals(pos, len(s)):

# 如果该文件并不是以写的方式打开的,那么改成读写的方式打开

if not self.whandles.has_key(file):

self.handles[file].close()

self.handles[file] = open(file, 'rb+')

self.whandles[file] = 1

h = self.handles[file]

# 通过 seek 函数移动文件指针,可以看出来,文件不是按照顺序来写的,因为所获取的文件片断是随机的,所以写也是随机的。

# 这里有一个疑问,假设获得了第二个文件片断,起始是 1000,大小是500,而第一个片断还没有获得,那么文件指针要移动到 1000 处,并写500个字节。这时候,文件的大小应该是 1500,尽管前面 1000 个字节是“空洞”。那么如果,直到结束,都没有获得第一个片断,又如何检测出来了?(通过检查 total?)

h.seek(begin)

h.write(s[total: total + end - begin])

total += end - begin

# 关闭所有打开文件

def close(self):

for h in self.handles.values():

h.close()

分享到:
评论

相关推荐

    node-v16.12.0-darwin-x64.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    试用Dev Containers的示例项目-Go

    计算机技术是指评价计算机系统的各种知识和技能的总称。它涵盖了计算机硬件、软件、网络和信息安全等方面。计算机技术的发展使我们能够进行高效的数据处理、信息存储和传输。现代计算机技术包括操作系统、数据库管理、编程语言、算法设计等。同时,人工智能、云计算和大数据等新兴技术也在不断推动计算机技术的进步。计算机技术的应用广泛,涵盖了各个领域,如商业、医疗、教育和娱乐等。随着计算机技术的不断革新,我们可以更加高效地实现预期自动化、标准化

    NTsky新闻发布v1.0测试版(提供JavaBean).zip

    ### 内容概要: 《NTsky新闻发布v1.0测试版》是一款基于 Java 开发的新闻发布系统的测试版本,旨在为新闻机构和媒体提供一个简单易用的新闻发布平台。该系统具有基本的新闻发布和管理功能,包括新闻分类、新闻编辑、新闻发布等核心功能。此外,该版本还提供了 JavaBean,使开发人员能够方便地将系统集成到自己的项目中,快速实现新闻发布的功能。 ### 适用人群: 本测试版本适用于新闻机构、媒体从业者以及Java开发人员。如果你是一家新闻机构或媒体,希望拥有一个简单易用的新闻发布平台,方便快捷地发布和管理新闻,那么这个测试版本将为你提供一个初步的体验。同时,如果你是一名Java开发人员,希望学习和掌握新闻发布系统的开发技术,并且对新闻行业有一定的了解,那么通过这个测试版本,你可以获取到一些JavaBean,并且可以参考系统的设计和实现,为你的项目开发提供参考和借鉴。无论是从业务需求还是技术学习的角度,该测试版本都将为你提供一定的帮助和支持。

    JavaScript介绍.zip

    javascript,JavaScript 最初由 Netscape 公司的 Brendan Eich 在 1995 年开发,用于 Netscape Navigator 浏览器。随着时间的推移,JavaScript 成为了网页开发中不可或缺的一部分,并且其应用范围已经远远超出了浏览器,成为了全栈开发的重要工具。

    15-21.php

    15-21.php

    汽车租赁系统(毕业设计)

    汽车租赁系统后端采用了spring,spring mvc,mybatis框架,前端使用了layui,界面美观。 包含功能:客户管理,车辆管理,出租,出租单管理,汽车入库,检查单管理,菜单管理,用户管理,角色管理,日志管理,统计分析等。 该毕业设计功能涵盖了大部分汽车租赁中的业务需求,特点是业务功能较多,有助于学生加深业务到技术的理解。

    设计模式_行为型_访问者模式.md

    设计模式_行为型_访问者模式

    HTML25-创意网站产品主页模板官网落地页APP主页产品宣传页源码 landing静态页面.zip

    HTML25-创意网站产品主页模板官网落地页APP主页产品宣传页源码 landing静态页面

    快手弹幕采集学习源码!!

    快手弹幕采集学习源码

    general-exporter windows

    自定义监控项 Windows 二进制文件

    数据可视化大屏展示系统HTML模板源码 大数据大屏展示源码 VUE.zip

    数据可视化大屏展示系统HTML模板源码 大数据大屏展示源码 VUE

    node-v18.2.0-linux-armv7l.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    这个项目是用于个人参加浙江大学移动创新竞赛而使用。.zip

    这个项目是用于个人参加浙江大学移动创新竞赛而使用。

    2023年全国职业院校技能大赛“区块链技术应用赛项”国赛正式赛题

    2023年全国职业院校技能大赛“区块链技术应用赛项”国赛正式赛题 高职 全国职业院校技能大赛 正式赛题

    基于stm32的智能家居系统

    基于stm32的智能家居系统 基于stm32的智能家居系统

    21九章空间解析几何.pdf

    21九章空间解析几何.pdf

    吴恩达机器学习python版本代码(完结).zip

    吴恩达机器学习python版本代码(完结)

    HTML21-印刷模板官网落地页APP主页产品宣传页源码 landing静态页面.zip

    HTML21-印刷模板官网落地页APP主页产品宣传页源码 landing静态页面

    信息办公简易java开源订销管理系统-javainfo.zip

    ### 内容概要: 《[信息办公]简易Java开源订销管理系统》是一款基于 Java 开发的开源项目,旨在为企业和机构提供一套简单易用的订购和销售管理解决方案。该系统具有简洁的界面设计和丰富的功能模块,包括客户管理、产品管理、订单管理、库存管理等核心功能,同时支持生成报表和统计分析,帮助用户快速了解企业的订购和销售情况,提高管理效率和业务决策能力。 ### 适用人群: 本项目适用于企业信息办公人员和Java开发人员。如果你是一位信息办公人员,希望通过一个简单易用的系统来管理企业的订购和销售业务,该系统将为你提供一个方便快捷的解决方案。同时,如果你是一名Java开发人员,希望学习和掌握Java开发技术,并且对企业信息管理系统感兴趣,那么这个开源项目将为你提供一个学习和实践的机会。无论是从业务管理还是技术学习的角度,该项目都将为你提供实用的功能和丰富的学习资源。

    [精品] todo_appAdobeXD源码下载设计素材UI设计.xd

    [精品] todo_appAdobeXD源码下载设计素材UI设计

Global site tag (gtag.js) - Google Analytics