【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.mzph.cn/pingmian/59608.shtml

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

相关文章

NVR小程序接入平台/设备EasyNVR多品牌NVR管理工具/设备汇聚公共资源场景方案全析

随着信息技术的飞速发展&#xff0c;视频监控已经成为现代社会安全管理和业务运营不可或缺的一部分。特别是在公共资源管理方面&#xff0c;视频监控的应用日益广泛&#xff0c;涵盖了智慧城市、智能交通、大型企业以及校园安防等多个领域。NVR小程序接入平台EasyNVR作为一款功…

C/C++使用AddressSanitizer检测内存错误

AddressSanitizer 是一种内存错误检测工具&#xff0c;编译时添加 -fsanitizeaddress 选项可以在运行时检测出非法内存访问&#xff0c;当发生段错误时&#xff0c;AddressSanitizer 会输出详细的错误报告&#xff0c;包括出错位置的代码行号和调用栈&#xff0c;有助于快速定位…

contenteditable实现需要一个像文本域一样的可编辑框

我这里是因为左上和右下有一个固定的模板&#xff0c;所有用textarea有点不方便&#xff0c;查了下还有一个方法可以解决就是在需要编辑的元素上加上 :contenteditable"true" 完整代码如下&#xff0c;因为这个弹窗是两用的&#xff0c;所以用messageType做了一下判…

QML项目实战:自定义Switch按钮

目录 一.添加模块 1.QtQuick.Controls 2.1 2.QtGraphicalEffects 1.12 二.自定义Switch 三.标签 四.效果 五.代码 一.添加模块 1.QtQuick.Controls 2.1 QtQuick.Controls 提供了一组预定义的 UI 控件&#xff0c;这些控件可以用于构建现代、响应式的用户界面。它包括按…

【数据集】【YOLO】【目标检测】电动车佩戴头盔检测数据集 5448 张,YOLO/VOC格式标注!

数据集介绍 【数据集】电动车头盔检测数据集 5448 张&#xff0c;目标检测&#xff0c;包含YOLO/VOC格式标注。数据集中包含3种分类&#xff0c;包含两轮电动车、戴头盔、不戴头盔。数据集来自国内外监控摄像头截图。检测范围电动车、摩托车、双轮非自行车。 一、数据概述 佩戴…

秃姐学AI系列之:GRU——门控循环单元 | LSTM——长短期记忆网络

RNN存在的问题 因为RNN模型的BPTT反向传导的链式求导&#xff0c;导致需要反复乘以一个也就是说会出现指数级别的问题&#xff1a; 梯度爆炸&#xff1a;如果的话&#xff0c;那么连乘的结果可能会快速增长&#xff0c;导致梯度爆炸梯度消失&#xff1a;如果的话&#xff0c;…

mysql if函数如何处理无匹配记录的情况?使用聚合函数

问题描述&#xff1a;编者在使用mysql中的if(car_number,"监管车辆","非监管车辆")函数时&#xff0c;场景为在一个car表中如果能查到具体某辆车这辆车就是我司监管车辆&#xff0c;差不到就不是我司监管车辆显示非监管车辆&#xff0c;遇到匹配不到的数据…

以太网交换安全:MAC地址漂移

一、什么是MAC地址漂移&#xff1f; MAC地址漂移是指设备上一个VLAN内有两个端口学习到同一个MAC地址&#xff0c;后学习到的MAC地址表项覆盖原MAC地址表项的现象。 MAC地址漂移的定义与现象 基本定义&#xff1a;MAC地址漂移发生在一个VLAN内的两个不同端口学习到相同的MAC地…

【react框架之dvajs】dvajs项目中effect互相调用及阻塞的实现方式

在dva中实现两个effect方法互相使用&#xff0c;即方法A处理完了接口请求&#xff0c;拿到相关数据再去用另外一个getor方法。像下面这样的效果 业务需求 effects: {*getA({ type, payload }, { put, take }) {yield put({ type: "getB" });yield put({ type: "…

Unity3D学习FPS游戏(10)子弹攻击敌人掉血(碰撞检测)

前言&#xff1a;前面最然创造出带有血条的敌人&#xff0c;但子弹打中敌人并没有效果。所以本篇将实现子弹攻击敌人&#xff0c;并让敌人掉血。 子弹攻击敌人掉血 整体思路目标补充知识-碰撞检测 准备工作刚体和碰撞器添加添加刚体后子弹代码优化补充知识-标签系统Tag添加 碰…

Tornado简单使用

Tornado简单使用 1 介绍 Tornado 是一个基于Python的Web服务框架和 异步网络库&#xff0c;它最初由 FriendFeed 开发&#xff0c;后来被 Facebook 收购并开源&#xff0c;通过利用非阻塞网络 I/O, Tornado 可以承载成千上万的活动连接&#xff0c;完美的实现了 长连接、WebS…

倍思获喜马拉雅年度最佳协作之星,打造移动数码品牌跨界新体验

近日,在“听见,共建,同行”——2024喜马拉雅有声之夜年度创作者大会暨峰爆榜颁奖典礼上,移动数码品牌Baseus倍思凭借其卓越的技术实力与创新的品牌理念,荣获“年度最佳协作之星”奖项。这一荣誉肯定了倍思在行业的深耕细作,也树立起品牌与喜马拉雅平台跨界合作、共同演绎音频生…

[单例模式]

[设计模式] 设计模式是软件工程中的一种常见做法, 它可以理解为"模板", 是针对一些常见的特定场景, 给出的一些比较好的固定的解决方案. 不同语言适用的设计模式是不一样的. 这里我们接下来要谈到的是java中典型的设计模式. 而且由于设计模式比较适合有一定编程经…

内部知识库:优化企业培训流程的关键驱动力

在当今快速变化的商业环境中&#xff0c;企业培训的重要性日益凸显。内部知识库作为整合、管理和分享企业内部学习资源的关键工具&#xff0c;正逐步成为优化企业培训流程的核心。以下将探讨内部知识库如何通过多种功能&#xff0c;助力企业提升培训效率、质量和员工满意度。 …

Ubuntu - 进入紧急模式,无法进入桌面

目录 一、问题 二、分析原因 三、解决 四、参考 一、问题 重新安装VMVare之后&#xff0c;将之前的虚拟机加载不进来 二、分析原因 查看系统错误日志 journalctl -xb | grep Failed mnt挂载找不到了 三、解决 查看系统错误日志 如果是磁盘错误&#xff0c;此时终端会有…

I.MX6U 裸机开发3. GPIO操作控制LED灯

I.MX6U 裸机开发3. GPIO操作控制LED灯 一、创建项目目录及源文件1. 新建目录2. 远程开发环境3. 创建源文件 二、代码编写1. 打开时钟2. 配置端口复用功能为GPIO3. 配置端口电气属性4. 设置GPIO方向&#xff08;GDIR寄存器&#xff09;5. 输出6. 死循环等待 三、编译程序1. 整体…

java ssm 公司内部员工管理系统 员工信息管理 企业员工 源码 jsp

一、项目简介 本项目是一套基于SSM的公司内部员工管理系统&#xff0c;主要针对计算机相关专业的和需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、数据库脚本、软件工具等。 项目都经过严格调试&#xff0c;确保可以运行&#xff01; 二、技术实现 ​后端技术&am…

数据分析:宏基因组DESeq2差异分析筛选差异物种

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍原理:计算步骤:结果:加载R包准备画图主题数据链接导入数据Differential abundance (No BP vs 2BP TA)构建`countData`矩阵过滤低丰度物种构建DESeq数据对象DESeq2差异分析画图Di…

Spark的yarn集群环境搭建

一.为什么要搭建yarn集群 为什么要将Spark的程序运行在YARN上&#xff0c;不运行在自带的 Standalone集群上&#xff1f; 1、统一化资源管理 Standalone是Spark专用的资源管理集群&#xff0c;只能用于运行 Spark程序 YARN是功能的分布式资源管理平台&#xff0c;可以运行各种分…

51单片机教程(六)- LED流水灯

1 项目分析 基于点亮LED灯、LED灯闪烁&#xff0c;扩展到构成最简单、花样流水灯。 2 技术准备 1 流水灯硬件及原理图 流水灯是由多个LED灯组成的 2 C语言知识点 数组 数组声明&#xff1a;长度不可变 数据类型 数组名称[长度n] // 整数型默认为0&#xff0c;小数型默认…