BT调查报告

目前,BT已经被网友推崇为“网络下载的里程碑”,因为它采用了一种具革命精神的设计思路——人人为我,我为人人。BT能够自动将一个文件分割成若干块,在配合集群下载方式以后,当我们建立了一个下载链接,自己的计算机同时也就成为了一个服务器,能够向其他网友提供已经下载的文件块。这样当我们下载某个文件的时候,实际上可能是来自若干台不同计算机的文件块在你的机器上“拼凑”成了最后的文件。

[特点]

其主要特点就是:下载人数越多,速度越快,不像传统集中式文件共享方式(如ftp)那样共享文件集中在服务器,下载速度,连接数量都有限制.

缺点是:严重吞噬带宽, 频繁读写硬盘,造成硬盘损耗。

[组成部分]

·               A static 'metainfo' file   .torrent文件

·               .torrent文件服务器

·               A BitTorrent tracker

·               An 'original' downloader

·               The end user web browsers

·               The end user downloaders

1.         .torrent file  包含

首先是 announce 记录了发布服务器的位置,BT知道是那个WEB服务器发布的,
然后是一些文件信息(info),文件名,目录名,长度等等
最后是片段长度,和片段的 Sha1 校验码。
(BT
为了实现续传和文件校验,就把文件分成若干个片段)

2.         .torrent 文件服务器

一般为WWW网站。

3.         A BitTorrent tracker

服务器中有个 track 程序来管理连接请求,它管理跟踪所有的客户端连接状况,发布

共享文件就往这里发布,它是BitTorrent的核心,所以需要是相对稳定的服务器。

4.         An 'original' downloader

“文件发布者”为第一个Client(client1) 包括所有完整的文件数据的Client称之

Seed--种子,“文件发布者”就成为第一个种子。如果有其他Client完整下载了所有文

件数据,那么它也就成为种子,这时“文件发布者”不在线了,也能继续提供完整下载。

5.         The end user web browsers

6.         The end user downloaders

下载用户只需按装下载客户端就行,它像是浏览器插件,点击.torrent文件的超链接,选择文件存放目录,就开始下载了。

[工作原理]

1) “文件发布者”将欲共享资源制作生成.torrent文件,往“Tracker服务器”发布。.torrent文件中包含了“Tracker服务器”的连接地址URL。“文件发布者”为第一个client(client1) 包括所有完整的文件数据的client称之为Seed种子,“文件发布者”就成为第一个种子。如果有其他client完整下载了所有文件数据,那么它也就成为种子,这时“文件发布者”不在线了,也能继续提供完整下载,因为“Tracker服务器”管理着所有客户端的连接情况,也因此“Tracker服务器”是最核心的、最重要的。该服务器提供了发布的统一管理,不像其它P2P软件那样到处去找那些非常不稳定的个人服务器,但不好的地方是announce当机的时候就无法下载了,它必须保证36524小时在线。

2) 当第二个人(client2)点击.torrent文件的超链接,开始下载的时候,会和"Tracker服务器"建立联系(因为.torrent文件中包含了“Tracker服务器”的URL)。从“Tracker服务器”上,取得已有client的信息(如client1),他会依照.torrent的文件划分规则开始从client1下载数据。比如将发布的文件分为10段,这时他开始下载第一段,当client3下载的时候“Tracker服务器”会告诉他从client2下载文件的第一段,而从client1下载文件的其他段,以次类推,每个client可以同时从其他Client那里下载他们已有的数据段,而下载的同时,也向其他client提供自己已下载的数据段,从而不仅提高了大家的下载速度也减轻了第一个release者的带宽压力。

[服务启动步骤]

1.                   启动tracker程序(多数情况下已经启动)

2.                   启动普通的web 服务器,如apache,(多数情况下已经启动)

3.                   web服务器上建立扩展名为.torrent的文件与mimetype的应用程序/x-bittorrent 的联系 (多数情况下已经完成).

4.                   将欲共享的文件制成.torrent 文件,并生成trackerURL

5.                   .torrent 文件放在web服务器

6.                   建立网页到.torrent文件的链接

7.                   启动文件发布者作为第一个客户端

[连接过程]

1.                     网站依旧提供静态文件,但会触发客户端的BT程序

2.                     Tracker得到所有下载者的信息,并将其他下载者的信息发送给他,通过HTTPHTTPS协议实现;

3.                     下载者周期与tracker交互,告知进展,并直接与其他下载者互联来实现上传下载,他们之间遵循BitTorrent peer protocol, 通过TCP协议进行通信.

4.                     文件发布者,作为特殊的客户端,他只提供上传,不下载,因为他已有完整的文件,称为种子,任何完成下载的客户端都可以成为种子。

 [BitTorrent协议详解]

1.         .torrent 文件和tracker的回复都是利用一种称为benoding的编码方法。

下面将一.torrent文件用记事本打开来观察benoding编码(为方便观察增加了换行缩进和注释)

-------------------------------------------------------------------------------------------------

d                 //dictionary的开始,下面开始key:value对,key以按字典顺序排序

  8:announce26:http://bt.5qzone.net:8000/  //tracker URL

  13:creation datei1105857891e          //创建日期

  4:info                             //文件信息

  d

    5:files                          //共享的为目录,包含多个文件

    l                              //文件列表开始

      d

         6:lengthi733245440e        //文件长度,in bytes

         4:path                    //文件路径

         l

           12:天书奇谈.avi

         e

      e

      d

        6:lengthi62464e

        4:path

        l

          16:天书奇谈简介.doc

        e

      e                   

    e                     //文件列表结束

  4:name8:天书奇谈        //代表默认的下载文件或存成目录的名字。它是纯粹建议性的。

  12:piece lengthi524288e   //块大小,除了最后一块零头一般为2的指数幂,大多为218 6:pieces27980:_5捼逾時w?JV>……            //后面的乱码是片段的Sha1校验码
------------------------------------------------------------------------------

 

2.         Tracker通过http get 报文的参数来接收请求, 返回becoded 应答信息

Tracker get:请求中包含如下关键字:

info_hash: torrent文件中的 info 部分的Sha1校验码,WEB通过它在发布列表找到对应的纪录
peer_id:
是下载者自身的标识,它是120和当前时间+全球的唯一标识码(GUID)Sha校验的前八位,20
port:
监听端口,官方默认的是从6881端口开始试,如果端口被占用则依次向后推一个端口找空闲端口,到6889端口为止。
IP:
你的ip地址(或DNS主机名),没有的话服务器会自己找到
uploaded downloaded:
你上传和下载了多少,服务器可以用它来做流量分析,编码为十进制ASCII码。
left:
你还要下载多少个字节,这个数不是通过文件长度和已下载数算出来的,因为文件可能在被续传,还有一些已经下载的数据不能通过完整性检查必须重新下载。
event:
状态,告诉服务器你是准备开始下载,还是停止,还是下载完成了
tracker request
操作默认 5 分钟做一次,或由服务器设定

 

3.         tracker 应答格式

得到这一串代码后就会用 info_hash 来查找列表,找到你就可以下载,找不到报错
接着它会反连(NatCheck)你的 IP(或DNS主机名) Port这样就可以知道你是内网用户还是共网用户(

果你是内网用户,它是连不通的,因为它会连到你的服务器上,你的服务器当然没有这个端口
)然后服务器返回现在正在下载这个文件的所有公网用户的IPport,就像是这样:

----------------------------------------------------------------------------------------------------------
d

8:intervali1800e            //rerequest interval 30分钟,peer不足时可不按此interval

5:peers

l

d

2:ip14:xxx.xxx.xx.xxx

7:peer id20:00180531904b7e3abdd7

4:port i6881e

e

e

e

----------------------------------------------------------------------------------------------------------

最后如果你是公网用户它会把你提交的 IP Port 放到info_hash 对应的列表中,
这样其它人就可以找到你

 

4.         BitTorrent's peer protocol

BT点对点协议建立在TCP之上,是对称的双向连接.peer按块下载,下载完成后,校验通过,则向所有同伴发消息.

连接的两端维护两个状态位:是否感兴趣,是否阻塞.若一端阻塞则停止发送数据,直到阻塞状态解除.当连接的一端感兴趣,而另一端没有阻塞就进行数据传输,是否感兴趣这一状态需要持续更新.若一端不再感兴趣或阻塞,连接即中断.

当数据传输时,下载者要备好多份请求,排成队列,以获得较高的TCP传输效率(这叫“管运请求”)。另一方面,不能被写入TCP缓冲区的请求要被立即排入内存,而不是一个应用程序级的网络缓冲,一旦阻塞出现,这些请求全部丢弃。

peer连线协议包括一次握手跟着不断的大小一致且确定的信息流。握手的开始是字符十九(十进制),跟着是字符串'BitTorrentprotocol'。开头的字符是长度固定的。

此后所有送入协议的整数都编码为4字节big-endian

然后是来自元信息文件中Becodedinfo值中长20字节的SHA1验证码(和info_hashTracker声明的值相同,但这里是原始值那里是引用)。如果双方的值不同,连接断开。一个例外是下载者想只用一个端口进行多个连接下载,它们会先从接入连接得到一个验证码,然后和列表里面的对照,有相同的就答复。

 

The peer wire protocol

 

19BitTorrent protocol

(becoded)

 

 

(Reserved)  0

 

sha1 hash of the bencoded form of the info value from the metainfo file

[1]

 

Peer ID[2]

stream of length prefixes and messages[3]

 

 

 

 

                  

 

 

For handshaking
[1]
若两端sha1 hash值不同切断连接

[2]若发起方期望的peer id与接收方的peer id不匹配,切断连接

[3]长度为零的message keepalive message

  non-keepalive messages 格式为

choke

 

unchoke

 

interested

 

uninterested

以上四种类型没有载荷

bitfield

1111110001010100011111…..

Bitfield只作为第一个消息传送,以传送的位置1,其余位置0开始下载时没有任何数据的下载者跳过“bitfield”信息。首字节高位到低位对应索引0-7,依次类推,第二字节对应8-15,等等。尾部的剩余的比特位设为0

have

number

下载完哪块并且该块通过校验

request

index

Begin(byte offsets)

Length(byte offsets)

length一般是2的权值除非被文件尾截断。现行一般是215次幂,并且关闭大于217次幂长度的连接。

cancel

index

Begin(byte offsets)

Length(byte offsets)

cancel”类信息载荷和“request”类信息一样。它通常在下载接近完成即“最后阶段”发出。当下载快要完成时,剩下几个块有都从同一个线程下载的趋向,这样会很慢。为了确保剩余块下载迅速,一旦还没有决定剩余块的下载请求向谁发出,先向所有他正在从对方下载数据的连接者发送要求所有剩余块的请求。为避免低效,每当一个块开始下载就向其他peer发出取消信息。

piece

index

Begin(byte offsets)

piece

piece”类信息包含一个索引,开始和块。记住它和“请求”类信息是相关的。当传输速度很慢或“choke”“unchoke”类信息高频率交替发出或两者同时发生,可能会载到一个不需要的块。

下载者下载块的顺序是随机的,这样适当防止下载者与其他Peers仅有相同的块子集或超集。

    下面描述的阻塞算法是目前基础的配置。重要的是所有新算法不光要在包含全部扩展算法的网络中运行良好,也要在主要包含这个基础算法的网络中运行良好。
   
一个优秀的阻塞算法有许多标准。它必须封锁一定同时上传的数量以获得良好的TCP表现,还要避免频繁的堵塞和通畅交替,即所谓“fibrillation”。它应该用数据交换报答给自己数据的peer。最后,它还应该偶尔尝试一下与未使用过的peer端连接,找出比现有连接好的连接,这叫做优化疏通。
   
现行的阻塞算法避免fibrillation的手段是每10秒更改一次阻塞状态。疏通4interested且能从他们身上得到最高下载速率的peer,进行上传和数据交换。有较高上传速率但是uninterestedpeer被疏通,一旦这些peer开始interested,那些上传率最低的peer的就被阻塞。如果下载者有了完整的文件,用它的上传率而不是下载率来决定疏通谁的连接。
   
在优化疏通中,任何一次中都有一个peer被疏通不管他的上传率如何(如果interested,他会成为4个提供下载的peer之一)。被优化疏通的这种peer30秒轮换一次。为了给它们一个上传整一个块的机会,新连接会以轮换中优化疏通次数的3倍开始连接。

 
<think>我们遇到了一个常见的JavaScript错误:`TypeError:Cannotreadpropertiesofundefined(reading'dclx')`。这个错误发生在尝试访问一个未定义对象的属性时。在Vue.js应用中,这通常发生在处理异步数据或响应式对象时,特别是当我们假设某个对象已经存在但实际上尚未定义或初始化的情况下。###问题分析根据报错信息,错误发生在以下位置:```javascript<divv-if="activeSurvey.value.dclx==='2'">```和```javascript<divv-if="activeSurvey.value.dclx==='3'">```以及```javascripttoInfo(item)```原因是在`activeSurvey.value`为`undefined`时,我们尝试访问它的`dclx`属性。这是因为`activeSurvey`的初始值并没有包含一个具有`value`属性的对象,或者我们在设置`activeSurvey`时没有正确赋值。###解决方案####1.初始化`activeSurvey`对象在代码中,`activeSurvey`被定义为:```javascriptconstactiveSurvey=ref({value:{id:'',qydcl:'',dclx:'',tffs:''}});```但请注意,我们使用`ref`包装了一个对象,该对象有一个`value`属性,而`value`属性才是我们需要的对象。然而,在模板中我们使用的是`activeSurvey.value.dclx`,这意味着我们期望`activeSurvey.value`是一个对象。但在初始化时,我们确实给了它一个对象。问题可能出在我们重置`activeSurvey`的时候。####2.修复`toInfo`函数中的赋值在`toInfo`函数中,我们这样设置`activeSurvey.value`:```javascriptactiveSurvey.value={id:survey.id,qydcl:survey.qydcl,dclx:survey.dclx,tffs:survey.tffs};```注意,我们直接给`activeSurvey.value`赋值了一个新对象,这个新对象没有`value`属性!所以当我们再次在模板中访问`activeSurvey.value.dclx`时,实际上相当于`activeSurvey.value`现在是新对象,而新对象没有`value`属性,所以`activeSurvey.value.value`就是`undefined`,进而导致访问`dclx`失败。###正确做法我们应该保持数据结构的一致性。既然我们初始化时`activeSurvey.value`是一个包含`id,qydcl,dclx,tffs`的对象,那么在更新时,我们也要更新这个对象的属性,而不是整个替换。####修改`toInfo`函数```javascriptconsttoInfo=(survey)=>{//使用Object.assign来更新activeSurvey.value的属性Object.assign(activeSurvey.value,{id:survey.id,qydcl:survey.qydcl,dclx:survey.dclx,tffs:survey.tffs});//其他代码不变...};```或者,如果我们想保持原来的结构(即`activeSurvey.value`是一个对象,这个对象有`id`等属性),那么我们在模板中就不应该再使用`activeSurvey.value.dclx`,而是直接`activeSurvey.dclx`。但是,由于我们使用`ref`,在模板中直接写`activeSurvey.dclx`是访问不到值的,因为`ref`对象在模板中会自动解包,所以我们应该用`activeSurvey.value.dclx`。所以,修改方法是将`activeSurvey`的定义改为:```javascript//修改activeSurvey的定义,ref中直接存放对象,而不是一个含有value属性的对象constactiveSurvey=ref({id:'',qydcl:'',dclx:'',tffs:''});```然后在`toInfo`中:```javascriptactiveSurvey.value={id:survey.id,qydcl:survey.qydcl,dclx:survey.dclx,tffs:survey.tffs};```这样,在模板中我们就可以使用`activeSurvey.dclx`(因为Vue模板中会自动解包ref,所以我们可以直接写`activeSurvey.dclx`,但是注意,在模板中,Vue会自动解包顶级ref,所以我们直接写`activeSurvey.dclx`即可)?不,实际上在模板中,我们仍然要使用`activeSurvey.value.dclx`吗?不是的,在模板中,当我们使用`ref`创建的响应式对象,如果在模板中直接使用,会自动解包内部值,所以我们可以这样写:修改模板:```html<divv-if="activeSurvey.dclx==='2'"><divv-if="activeSurvey.dclx==='3'">```同时,修改`activeSurvey`的初始化:```javascriptconstactiveSurvey=ref({id:'',qydcl:'',dclx:'',tffs:''});```在`toInfo`函数中:```javascriptactiveSurvey.value={id:survey.id,qydcl:survey.qydcl,dclx:survey.dclx,tffs:survey.tffs};```这样,模板中访问的就是`activeSurvey.dclx`,而`activeSurvey`是一个ref,在模板中会自动解包,所以实际访问的是`activeSurvey.value.dclx`(Vue内部自动处理)。或者,另一种做法,我们不在模板中解包,而使用`activeSurvey.value.dclx`,那么我们就必须确保`activeSurvey.value`永远是一个对象。因此,初始化时:```javascriptconstactiveSurvey=ref({value:{id:'',qydcl:'',dclx:'',tffs:''}});```但这样,在模板中我们必须使用`activeSurvey.value.value.dclx`?这显然不合理。因此,推荐使用第一种方法:将`activeSurvey`定义为一个ref,其值是一个对象,然后在模板中直接使用`activeSurvey.dclx`(自动解包)。这样代码更清晰。###具体修改步骤1.修改`activeSurvey`的定义```javascriptconstactiveSurvey=ref({id:'',qydcl:'',dclx:'',tffs:''});```2.修改模板中的条件判断```html<divv-if="activeSurvey.dclx==='2'"><divv-if="activeSurvey.dclx==='3'">```3.在`toInfo`函数中,更新`activeSurvey`的方式不变(直接赋值整个对象):```javascriptactiveSurvey.value={id:survey.id,qydcl:survey.qydcl,dclx:survey.dclx,tffs:survey.tffs};```4.同时,我们仍然需要确保在调用`toInfo`时传入的`survey`对象有`dclx`属性,所以我们在模板中遍历`list`时,确保`item`有`dclx`属性。###额外的防御性编程在模板中使用可选链操作符(如果Vue版本支持):```html<divv-if="activeSurvey.dclx==='2'">```可以改为:```html<divv-if="activeSurvey?.dclx==='2'">```这样即使`activeSurvey`为`null`或`undefined`也不会报错。但在我们的情况中,`activeSurvey`初始化为一个对象,所以不会为`null/undefined`,所以主要是确保`dclx`属性存在。###总结主要问题在于我们错误地设置了`activeSurvey`的结构。在初始化时,我们将其设置为一个具有`value`属性的对象,但在更新时却用一个新的对象覆盖了`activeSurvey.value`,导致它不再具有`value`属性。通过调整初始化结构,并保持模板中访问的一致性,我们可以解决这个问题。修改后的代码应该能避免这个错误,同时保持逻辑正确。§§相关问题§§1.Vue3中ref和reactive的使用场景和区别是什么?2.如何在Vue中安全地访问嵌套对象的属性?3.JavaScript中可选链操作符(?.)的使用场景有哪些?</think> 在 Vue.js 中安全地访问对象属性以避免 "Cannot read properties of undefined" 错误,特别是在处理后端返回的数据时,可以采取以下策略: ### 1. 使用可选链操作符(?.)[^1] ```html <!-- 修改模板中的访问方式 --> <div v-if="activeSurvey?.dclx === '2'"> <div v-if="activeSurvey?.dclx === '3'"> ``` ```javascript // 在 JavaScript 中的可选链 const dclx = response.data?.root?.[0]?.dclx ``` ### 2. 添加防御性空值检查[^2] ```javascript const toInfo = (survey) => { if (!survey || !survey.hasOwnProperty('dclx')) { console.error('无效的survey对象'); return; } activeSurvey.value = { id: survey.id || '', qydcl: survey.qydcl || '', dclx: survey.dclx || '', tffs: survey.tffs || '' }; } ``` ### 3. 初始化响应式对象 ```javascript // 确保对象结构完整 const activeSurvey = ref({ id: '', qydcl: '', dclx: '', tffs: '' }); ``` ### 4. 在后端API处理中添加数据校验 ```javascript const loadSurveyList = async () => { try { const response = await axios.post('/api/wechat/getVoteList'); // 添加数据校验和过滤 const isValidData = response.data?.root?.every(item => item.id && item.dclx && item.kssj && item.jssj ); if (!isValidData) { showDialog({ message: '数据格式错误' }); return; } list.value = response.data.root || []; } catch (error) { console.error('数据获取失败:', error); } }; ``` ### 5. 使用计算属性处理复杂逻辑 ```javascript const dialogTitle = computed(() => { return activeSurvey.value?.dclx === '2' ? '身份验证' : activeSurvey.value?.dclx === '3' ? '调查令验证' : '验证信息'; }); ``` ### 6. 添加全局错误处理 ```javascript app.config.errorHandler = (err, vm, info) => { if (err.message.includes('Cannot read properties of undefined')) { console.error('未定义属性访问错误:', err.message); // 可添加自动重试或错误报告逻辑 } } ``` ### 7. 使用 TypeScript 定义接口 ```typescript // 定义后端返回数据接口 interface SurveyItem { id: string; bt: string; kssj: string; jssj: string; dclx: '0' | '1' | '2' | '3'; qydcl: string; tffs: string; } interface ApiResponse { api_status: boolean; root: SurveyItem[]; } ``` ### 8. 在模板中使用 v-if 双重保护 ```html <template v-if="activeSurvey"> <div v-if="activeSurvey.dclx === '2'"> <!-- 身份验证字段 --> </div> <div v-if="activeSurvey.dclx === '3'"> <!-- 调查令验证字段 --> </div> </template> ``` 通过结合以上策略,可以有效地防止 "Cannot read properties of undefined" 错误,特别是当后端返回的数据结构可能变化时。推荐优先使用可选链操作符和防御性检查相结合的方法[^1][^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值