【p2p、分布式,区块链笔记 Torrent】WebTorrent 的lt_donthave插件

扩展实现

  • https://github.com/webtorrent/lt_donthave/blob/master/index.js
/*! lt_donthave. MIT License. WebTorrent LLC <https://webtorrent.io/opensource> */// 导入所需模块
import arrayRemove from 'unordered-array-remove' // 用于从数组中删除元素的函数
import { EventEmitter } from 'events' // 导入事件发射器类
import debugFactory from 'debug' // 导入调试工具// 创建一个调试实例,用于记录调试信息
const debug = debugFactory('lt_donthave')// 导出一个函数,该函数返回 ltDontHave 类
export default () => {// 定义 ltDontHave 类,继承自 EventEmitterclass ltDontHave extends EventEmitter {constructor (wire) {super() // 调用父类构造函数// 初始化属性this._peerSupports = false // 标记对等体是否支持 'lt_donthave'this._wire = wire // 保存 wire 对象,表示与对等体的连接}// 当接收到扩展握手时调用,表示对等体支持 'lt_donthave'onExtendedHandshake () {this._peerSupports = true}// 处理接收到的消息onMessage (buf) {let index // 存储块索引try {// 创建 DataView 从接收到的缓冲区中读取数据const view = new DataView(buf.buffer)index = view.getUint32(0) // 获取消息中的块索引} catch (err) {// 如果消息无效,直接丢弃return}// 如果对等体没有该块,直接返回if (!this._wire.peerPieces.get(index)) returndebug('got donthave %d', index) // 记录调试信息this._wire.peerPieces.set(index, false) // 标记该块为不拥有this.emit('donthave', index) // 触发 'donthave' 事件this._failRequests(index) // 处理失败的请求}// 向远程对等体发送不拥有的块索引donthave (index) {if (!this._peerSupports) return // 如果对等体不支持,直接返回debug('donthave %d', index) // 记录调试信息const buf = new Uint8Array(4) // 创建一个 4 字节的缓冲区const view = new DataView(buf.buffer) // 创建 DataViewview.setUint32(0, index) // 将块索引写入缓冲区console.log(">>>>>>>>>>>>>>>>", index, ">>>>>>>>>>>>>>", buf) // 打印调试信息this._wire.extended('lt_donthave', buf) // 发送 'lt_donthave' 消息}// 处理失败的请求_failRequests (index) {const requests = this._wire.requests // 获取当前请求列表for (let i = 0; i < requests.length; i++) {const req = requests[i] // 获取当前请求if (req.piece === index) { // 如果请求的块索引与指定索引匹配arrayRemove(requests, i) // 从请求列表中删除该请求i -= 1 // 更新索引以检查新值this._wire._callback(req, new Error('peer sent donthave'), null) // 调用回调函数,报告请求失败}}}}// 设置扩展名称ltDontHave.prototype.name = 'lt_donthave'return ltDontHave // 返回定义的类
}

测试代码

import fixtures from 'webtorrent-fixtures'
import Protocol from 'bittorrent-protocol'
import test from 'tape'
import ltDontHave from './index.js'const { leaves } = fixturesconst id1 = Buffer.from('01234567890123456789')
const id2 = Buffer.from('12345678901234567890')const wire1 = new Protocol()
wire1.peerPieces.set(30, true) // 在 wire1 的 peerPieces 中标记块 30 为已拥有
wire1.peerPieces.set(29, false)
console.log("wire1 中的块 30 是否存在",wire1.peerPieces.get(30))const wire2 = new Protocol()wire1.pipe(wire2).pipe(wire1)// 将 wire1 和 wire2 连接起来,形成双向数据流
wire1.use(ltDontHave())
wire2.use(ltDontHave())// 设置 wire2 的握手事件处理程序
wire2.on('handshake', (infoHash, peerId, extensions) => {// 在握手完成后,调用 wire2 的 handshake 方法console.log("2. wire2.on('handshake') infoHash:",infoHash,"peerId:", peerId)console.log("3. 在握手完成后,调用 wire2 的 handshake 方法,infoHash:",leaves.parsedTorrent.infoHash,"peerid:", id2)wire2.handshake(leaves.parsedTorrent.infoHash, id2)
})// 设置 wire2 的扩展事件处理程序
wire2.on('extended', ext => {if (ext === 'handshake') {     // 如果扩展事件是握手console.log("4. wire2 extended 发送 donthaven 消息,表示 wire2 不拥有块 30")wire2.lt_donthave.donthave(30)// 发送 "donthave" 消息,表示 wire2 不拥有块 30wire2.lt_donthave.donthave(29)}
})// 设置 wire1 的 "donthave" 消息事件处理程序(由protocol的index中的this._wire.extended('lt_donthave', buf)决定是否调用)
wire1.lt_donthave.on('donthave', (index) => {// 验证接收到的索引是否为 30console.log("5. wire1.lt_donthave.on('donthave') 接收到的索引为:",index)//t.equal(index, 30)// 检查 wire1 中的块 30 是否被清除console.log("6. wire1 中的块 30 是否存在",wire1.peerPieces.get(30))//t.notOk(wire1.peerPieces.get(30), 'piece 30 cleared in bitfield')console.log("6. wire1 中的块 29 是否存在",wire1.peerPieces.get(29))
})// 在 wire1 上执行握手,传入信息哈希和对等体 ID
console.log("1. 在 wire1 上执行握手,传入infoHash(",leaves.parsedTorrent.infoHash,") 和 peerId(", id1,")")
wire1.handshake(leaves.parsedTorrent.infoHash, id1)

测试输出

PS C:\Users\kingchuxing\Documents\MTGIT\lt_donthave-master> node .\mtest.js
wire1 中的块 30 是否存在 true
1. 在 wire1 上执行握手,传入infoHash( d2474e86c95b19b8bcfdb92bc12c9d44667cfa36 )peerId( <Buffer 30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39> )
2. wire2.on('handshake') infoHash: d2474e86c95b19b8bcfdb92bc12c9d44667cfa36 peerId: 3031323334353637383930313233343536373839
3. 在握手完成后,调用 wire2 的 handshake 方法,infoHash: d2474e86c95b19b8bcfdb92bc12c9d44667cfa36 peerid: <Buffer 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30>
4. wire2 extended 发送 donthaven 消息,表示 wire2 不拥有块 30
>>>>>>>>>>>>>>>> 30 >>>>>>>>>>>>>> Uint8Array(4) [ 0, 0, 0, 30 ]
>>>>>>>>>>>>>>>> 29 >>>>>>>>>>>>>> Uint8Array(4) [ 0, 0, 0, 29 ]
5. wire1.lt_donthave.on('donthave') 接收到的索引为: 30
6. wire1 中的块 30 是否存在 false
6. wire1 中的块 29 是否存在 false
  • lt_donthave插件的对某个index进行false标记过程:
  • wire1进行握手wire1.handshake(leaves.parsedTorrent.infoHash, id1),执行Wire对象的handshake (infoHash, peerId, extensions)函数,但是没有执行
    if (this.peerExtensions.extended && !this._extendedHandshakeSent) {// Peer's handshake indicated support already// (incoming connection)this._sendExtendedHandshake()}
  • 后续会执行_onHandshake,并在中途触发wire2.on('handshake',然后执行wire2的handshake。
    在这里插入图片描述
// 设置 wire2 的握手事件处理程序
wire2.on('handshake', (infoHash, peerId, extensions) => {// 在握手完成后,调用 wire2 的 handshake 方法console.log("2. wire2.on('handshake') infoHash:",infoHash,"peerId:", peerId)console.log("3. 在握手完成后,调用 wire2 的 handshake 方法,infoHash:",leaves.parsedTorrent.infoHash,"peerid:", id2)wire2.handshake(leaves.parsedTorrent.infoHash, id2)
})
  • 但是,与上次不同,这次的握手会调用_sendExtendedHandshake(),然后调用_onMessage方法

在这里插入图片描述

_onMessage

  • _onMessage 方法用于解析并处理从远程对等体接收到的不同类型的消息。它根据消息的第一个字节(标识消息类型)来调用相应的处理函数,确保能够正确响应对等体的请求和状态变化。对于未知的消息类型,会记录调试信息并触发相应事件。

在这里插入图片描述
在这里插入图片描述

/*** Handle a message from the remote peer.* @param  {Uint8Array} buffer  // 接收到的消息缓冲区,类型为 Uint8Array*/
_onMessage (buffer) {// 解析消息的长度,调用 _onMessageLength 方法处理this._parse(4, this._onMessageLength)// 创建 DataView,用于从 buffer 中以不同格式读取数据const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength)// 根据消息的第一个字节决定处理的方式switch (buffer[0]) {case 0:  // "choke" 消息return this._onChoke()case 1:  // "unchoke" 消息return this._onUnchoke()case 2:  // "interested" 消息return this._onInterested()case 3:  // "uninterested" 消息return this._onUninterested()case 4:  // "have" 消息return this._onHave(view.getUint32(1))  // 获取块索引并处理case 5:  // "bitfield" 消息return this._onBitField(buffer.slice(1))  // 处理位图数据case 6:  // "request" 消息return this._onRequest(view.getUint32(1),  // 获取块索引view.getUint32(5),  // 获取块的偏移view.getUint32(9)   // 获取块的长度)case 7:  // "piece" 消息return this._onPiece(view.getUint32(1),  // 获取块索引view.getUint32(5),  // 获取块的偏移buffer.slice(9)     // 获取块数据)case 8:  // "cancel" 消息return this._onCancel(view.getUint32(1),  // 获取块索引view.getUint32(5),  // 获取块的偏移view.getUint32(9)   // 获取块的长度)case 9:  // "port" 消息return this._onPort(view.getUint16(1))  // 获取端口号并处理case 0x0D:  // "suggest" 消息return this._onSuggest(view.getUint32(1))  // 获取建议的块索引case 0x0E:  // "have all" 消息return this._onHaveAll()  // 处理拥有所有块的消息case 0x0F:  // "have none" 消息return this._onHaveNone()  // 处理不拥有任何块的消息case 0x10:  // "reject" 消息return this._onReject(view.getUint32(1),  // 获取被拒绝的块索引view.getUint32(5),  // 获取偏移view.getUint32(9)   // 获取长度)case 0x11:  // "allowed fast" 消息return this._onAllowedFast(view.getUint32(1))  // 获取可以快速请求的块索引case 20:  // "extended" 消息return this._onExtended(buffer[1], buffer.slice(2))  // 处理扩展消息default:  // 未知消息类型this._debug('got unknown message')  // 输出调试信息return this.emit('unknownmessage', buffer)  // 触发未知消息事件}
}

_onExtended

  • _onExtended 方法处理来自远程对等体的扩展消息。首先,它检查是否为扩展握手消息(ext === 0),然后解码并保存握手信息,处理扩展映射并调用相应的扩展处理器。如果消息是其他类型的扩展消息,则将其传递给相应的处理器。最后,它触发与扩展相关的事件,方便其他模块进行相应处理。

在这里插入图片描述

  • 以下是代码实现:
_onExtended (ext, buf) {// 检查扩展类型是否为0,表示扩展握手消息if (ext === 0) {let infotry {// 尝试解码扩展握手消息的内容info = bencode.decode(buf)} catch (err) {// 如果解码失败,输出调试信息并忽略该消息this._debug('ignoring invalid extended handshake: %s', err.message || err)}// 如果信息为空,直接返回if (!info) return// 保存对等体的扩展握手信息this.peerExtendedHandshake = info// 检查握手信息中的扩展映射if (typeof info.m === 'object') {// 遍历每个扩展名,将其转换为数字并存储for (const name in info.m) {this.peerExtendedMapping[name] = Number(info.m[name].toString())}}// 遍历已注册的扩展for (const name in this._ext) {// 如果对等体支持该扩展,则调用其握手处理方法if (this.peerExtendedMapping[name]) {this._ext[name].onExtendedHandshake(this.peerExtendedHandshake)}}// 输出调试信息,表示收到了扩展握手this._debug('got extended handshake')// 触发扩展握手事件,即lt_donthave的wire2.on('extended', ext => {this.emit('extended', 'handshake', this.peerExtendedHandshake)} else {// 如果扩展类型不是0,则处理其他扩展消息if (this.extendedMapping[ext]) {// 将扩展类型转换为友好的名称ext = this.extendedMapping[ext] // 检查是否有注册的扩展处理器if (this._ext[ext]) {// 调用对应扩展的消息处理方法this._ext[ext].onMessage(buf)}}// 输出调试信息,显示收到的扩展消息类型this._debug('got extended message ext=%s', ext)// 触发扩展消息事件this.emit('extended', ext, buf)}
}
  • 以上代码的第46行this._ext[ext].onMessage(buf)调用onMessage方法
  • lt_donthave的onMessage方法(扩展中只有这个函数操作了标记是否有数据的peerPieces对象):
    onMessage (buf) {let indextry {const view = new DataView(buf.buffer)index = view.getUint32(0)} catch (err) {// drop invalid messagesreturn}if (!this._wire.peerPieces.get(index)) returndebug('got donthave %d', index)this._wire.peerPieces.set(index, false)this.emit('donthave', index)this._failRequests(index)}

使用示例

import BitField from 'bitfield'
import Protocol from 'bittorrent-protocol'
import net from 'net'net.createServer(socket => {var wire = new Protocol()socket.pipe(wire).pipe(socket)// handle handshakewire.on('handshake', (infoHash, peerId) => {wire.handshake(Buffer.from('my info hash'), Buffer.from('my peer id'))// advertise that we have all 10 pieces of the torrentconst bitfield = new BitField(10)for (let i = 0; i <= 10; i++) {bitfield.set(i, true)}wire.bitfield(bitfield)})}).listen(6881)
  • npm install lt_donthave
import BitField from 'bitfield'
import Protocol from 'bittorrent-protocol'
import net from 'net'
import lt_donthave from 'lt_donthave'net.createServer(socket => {const wire = new Protocol()socket.pipe(wire).pipe(socket)// initialize the extensionwire.use(lt_donthave())// all `lt_donthave` functionality can now be accessed at wire.lt_donthavewire.on('request', (pieceIndex, offset, length, cb) => {// whoops, turns out we don't have any pieces after allwire.lt_donthave.donthave(pieceIndex)cb(new Error('not found'))})// 'donthave' event will fire when the remote peer indicates it no longer has a piecewire.lt_donthave.on('donthave', index => {// remote peer no longer has piece `index`})// handle handshakewire.on('handshake', (infoHash, peerId) => {wire.handshake(Buffer.from('my info hash'), Buffer.from('my peer id'))// advertise that we have all 10 pieces of the torrentconst bitfield = new BitField(10)for (let i = 0; i <= 10; i++) {bitfield.set(i, true)}wire.bitfield(bitfield)})}).listen(6881)

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/466804.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

兰空图床配置域名访问

图床已经创建完毕并且可以访问了&#xff0c;但是使用IP地址多少还是差点意思&#xff0c;而且不方便记忆&#xff0c;而NAT模式又没法直接像普通服务器一样DNS解析完就可以访问。 尝试了很多办法&#xff0c;nginx配置了半天也没配好&#xff0c;索性直接重定向&#xff0c;反…

Sophos | 网络安全

在 SophosLabs 和 SophosAI 的威胁情报、人工智能和机器学习的支持下&#xff0c;Sophos 提供广泛的高级产品和服务组合&#xff0c;以保护用户、网络和端点免受勒索软件、恶意软件、漏洞利用、网络钓鱼和各种其他网络攻击。Sophos 提供单一的集成式基于云的管理控制台 Sophos …

STM32外设之SPI的介绍

### STM32外设之SPI的介绍 SPI&#xff08;Serial Peripheral Interface&#xff09;是一种高速的&#xff0c;全双工&#xff0c;同步的通信总线&#xff0c;主要用于EEPROM、FLASH、实时时钟、AD转换器等外设的通信。SPI通信只需要四根线&#xff0c;节约了芯片的管脚&#x…

基于 Transformer 的语言模型

基于 Transformer 的语言模型 Transformer 是一类基于注意力机制&#xff08;Attention&#xff09;的模块化构建的神经网络结构。给定一个序列&#xff0c;Transformer 将一定数量的历史状态和当前状态同时输入&#xff0c;然后进行加权相加。对历史状态和当前状态进行“通盘…

图数据库| 2 、大数据的演进和数据库的进阶——从数据到大数据、快数据,再到深数据

时至今日&#xff0c;大数据已无处不在&#xff0c;所有行业都在经受大数据的洗礼。但同时我们也发现&#xff0c;不同于传统关系型数据库的表模型&#xff0c;现实世界是非常丰富、高维且相互关联的。此外&#xff0c;我们一旦理解了大数据的演进历程以及对数据库进阶的强需求…

深度学习笔记10-多分类

多分类和softmax回归 在多分类问题中&#xff0c;一个样本会被划分到三个或更多的类别中&#xff0c;可以使用多个二分类模型或一个多分类模型&#xff0c;这两种方式解决多分类问题。 1.基于二分类模型的多分类 直接基于二分类模型解决多分类任务&#xff0c;对于多分类中的每…

一篇文章入门docker!

文章目录 DockerUbuntu 下 docker 安装安装docker运行docker Docker的常用命令帮助命令镜像命令容器命令其他常用命令小结 分层理解一、Docker镜像的分层结构二、UnionFS与镜像分层三、镜像层的具体内容四、镜像分层的好处五、容器层与镜像层的关系 如何提交一个自己的镜像 Doc…

鸿蒙(Harmony)实现滑块验证码

在Android和ios两端已经使用的滑块验证码框架还未适配鸿蒙版&#xff0c;于是需要自己去实现类似如下的滑块验证码&#xff1a; 那么实现这样的验证码主要涉及到几个内容&#xff1a; 1、自定义弹窗 2、base64图片转换 3、滑动组件与滑块的联动&#xff0c;以及横移距离转换…

什么是嵌入式操作系统?

什么是嵌入式操作系统? 想象一下&#xff0c;如果一个智能设备&#xff0c;比如你口袋里的智能手机&#xff0c;是一个有头脑的机器人&#xff0c;那么嵌入式操作系统&#xff08;Embedded Operating System&#xff0c;简称EOS&#xff09;就相当于这个机器人的大脑。它告诉机…

后台管理系统窗体程序:评论管理

目录 评论管理的功能介绍&#xff1a; 1、进入页面 2、页面内的各种功能设计 &#xff08;1&#xff09;网页内的表格 &#xff08;2&#xff09;拒绝按钮&#xff0c;批准按钮 &#xff08;3&#xff09;删除按钮 &#xff08;4&#xff09;页面翻页跳转按钮 一、网页设计​…

nginx代理 proxy_pass

一、location 包含 location /api/ {proxy_pass http://127.0.0.1:85;} 二、location 不包含 location /api/ {proxy_pass http://127.0.0.1:85/;} 三、locaion 包含 location /api {proxy_pass http://127.0.0.1:85;}四、location 包含 location /api {proxy_pass http://127.…

InnoDB 存储引擎<七>通用表空间+临时表空间

目录 通⽤表空间 - General Tablespace 临时表空间 - Temporary Tablespaces 通⽤表空间 - General Tablespace 对应磁盘上的文件需要用户手动创建 1.通⽤表空间的作⽤和特性&#xff1f; 解答问题&#xff1a; 1.作用&#xff1a;可以把数据量比较小且强相关的表&#xff…

乐维网管平台(五):如何精准定位网络终端设备

在当今数字化高度发展的时代&#xff0c;网络已经成为企业和组织运营的关键基础设施。而在网络管理领域&#xff0c;终端定位技术正发挥着越来越重要的作用。 一、什么是终端定位 终端定位是网络管理中的关键环节&#xff0c;从本质上讲&#xff0c;它是一种精确确定网络终端…

企业邮箱后缀设置指南,轻松融入公司品牌

邮箱后缀指""后域名&#xff0c;本文介绍如何添加公司名作为后缀&#xff0c;以Zoho邮箱为例&#xff0c;需注册账号、购买域名、配置DNS、添加自定义域名、创建账号。Zoho邮箱安全可靠、个性化定制、易于管理&#xff0c;提供不同定价方案&#xff0c;并给出客户端配…

【D3.js in Action 3 精译_039】4.3 D3 面积图的绘制方法及其边界标签的添加

当前内容所在位置&#xff1a; 第四章 直线、曲线与弧线的绘制 ✔️ 4.1 坐标轴的创建&#xff08;上篇&#xff09; 4.1.1 D3 中的边距约定&#xff08;中篇&#xff09;4.1.2 坐标轴的生成&#xff08;中篇&#xff09; 4.1.2.1 比例尺的声明&#xff08;中篇&#xff09;4.1…

时序动作定位 | 基于层次结构潜在注意模型的弱监督动作定位(ICCV 2023)

<Weakly-Supervised Action Localization by Hierarchically-structured Latent Attention Modeling> 这篇文章的标题是《Weakly-Supervised Action Localization by Hierarchically-structured Latent Attention Modeling》,作者是Guiqin Wang等人,来自西安交通大学和…

华为交换机Vlan划分

华为交换机Vlan划分 Tip&#xff1a;一个广播域划分vlan达到隔离广播目的且不能互访。 一个广播域划分子网掩码也可以不能互访&#xff0c;但是还是在一个广播域&#xff0c;还是会发生广播风暴。 本次实验模拟交换机不同端口划分不同vlan达到隔绝广播风暴效果。 pc1 pc2分配…

[linux]docker快速入门

安装 docker官网: CentOS | Docker Docs 准备工作: 准备ConstOS7的虚拟机环境账密: root/root飞书文档: Docs 卸载旧版本 // 首先如果系统中已经存在旧的Docker&#xff0c;则先卸载 yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest…

vue echarts左右间距调整 左右空白

咱就说这样的左右间距丑不丑。。 经过调整后&#xff0c;嗯&#xff0c;好看了很多。页面也协调多了&#xff01; 直接上代码&#xff1a;添加以下配置数据&#xff1a; grid: {x: 50,y: 25,x2: 30,y2: 35 }, this.chart.setOption({width: 100%,xAxis: {show: false,type: ca…

内置函数【MySQL】

文章目录 日期函数字符串函数数学函数其他函数 日期函数 current_date函数用于获取当前的日期 mysql> select current_date(); ---------------- | current_date() | ---------------- | 2024-11-03 | ---------------- 1 row in set (0.00 sec)current_time函数用于获…