当前位置: 首页 > news >正文

【p2p、分布式,区块链笔记 Torrent】: WebTorrent GitTorrent bittorrent-dht

bittorrent-dht模块

  • BitTorrent DHT 通过 DHT 网络广播值,允许其他用户通过 DHT 来发现和获取这些数据。

1. 导入依赖

var DHT = require('bittorrent-dht')

2. 创建实例

var dht = new DHT({bootstrap: config.dht.bootstrap
})
dht.listen(config.dht.listen)
  • new DHT({...}):创建一个新的 DHT 实例,bootstrap 参数用于指定引导节点的地址,允许客户端加入 DHT 网络。

  • dht.listen(...):在指定的端口上开始监听来自其他 DHT 节点的请求。

  • 处理 DHT 准备就绪事件

dht.on('ready', function () {// 处理
})
  • 示例代码
var DHT = require("bittorrent-dht");
var dht = new DHT();var dht = new DHT();
var value = new Buffer(200).fill("abc");dht.on("ready", function () {dht.put({ v: value }, function (errors, hash) {console.error("errors=", errors);console.log("hash=", hash);});
});setTimeout(function () {var arr = dht.toArray(); // 获取DHT节点数组console.log("DHT节点数组:", arr); // 打印数组到控制台dht.destroy();
}, 100000);
  • 输出:
➜  workspace git:(master) ✗ node index.js
(node:17667) [DEP0005] DeprecationWarning: Buffer() is deprecated due to security and usability issues. Please use the Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() methods instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
errors= [Error: 203 missing 'token' keyat DHT._onResponseOrError (/project/workspace/node_modules/bittorrent-dht/client.js:1077:11)at DHT._onData (/project/workspace/node_modules/bittorrent-dht/client.js:1037:10)at Socket.emit (node:events:518:28)at UDP.onMessage [as onmessage] (node:dgram:942:8) {address: '95.72.172.196:6881'},Error: 203 missing 'token' keyat DHT._onResponseOrError (/project/workspace/node_modules/bittorrent-dht/client.js:1077:11)at DHT._onData (/project/workspace/node_modules/bittorrent-dht/client.js:1037:10)at Socket.emit (node:events:518:28)at UDP.onMessage [as onmessage] (node:dgram:942:8) {address: '5.49.12.204:40333'},Error: 203 missing 'token' keyat DHT._onResponseOrError (/project/workspace/node_modules/bittorrent-dht/client.js:1077:11)at DHT._onData (/project/workspace/node_modules/bittorrent-dht/client.js:1037:10)at Socket.emit (node:events:518:28)at UDP.onMessage [as onmessage] (node:dgram:942:8) {address: '95.29.240.208:6881'},Error: 203 missing 'token' keyat DHT._onResponseOrError (/project/workspace/node_modules/bittorrent-dht/client.js:1077:11)at DHT._onData (/project/workspace/node_modules/bittorrent-dht/client.js:1037:10)at Socket.emit (node:events:518:28)at UDP.onMessage [as onmessage] (node:dgram:942:8) {address: '109.175.109.86:8893'},Error: 202 Server Errorat DHT._onResponseOrError (/project/workspace/node_modules/bittorrent-dht/client.js:1077:11)at DHT._onData (/project/workspace/node_modules/bittorrent-dht/client.js:1037:10)at Socket.emit (node:events:518:28)at UDP.onMessage [as onmessage] (node:dgram:942:8) {address: '146.19.24.245:23544'},Error: 203 missing 'token' keyat DHT._onResponseOrError (/project/workspace/node_modules/bittorrent-dht/client.js:1077:11)at DHT._onData (/project/workspace/node_modules/bittorrent-dht/client.js:1037:10)at Socket.emit (node:events:518:28)at UDP.onMessage [as onmessage] (node:dgram:942:8) {address: '81.101.23.157:56418'},Error: 203 missing 'token' keyat DHT._onResponseOrError (/project/workspace/node_modules/bittorrent-dht/client.js:1077:11)at DHT._onData (/project/workspace/node_modules/bittorrent-dht/client.js:1037:10)at Socket.emit (node:events:518:28)at UDP.onMessage [as onmessage] (node:dgram:942:8) {address: '159.146.86.15:42164'},Error: 203 missing 'token' keyat DHT._onResponseOrError (/project/workspace/node_modules/bittorrent-dht/client.js:1077:11)at DHT._onData (/project/workspace/node_modules/bittorrent-dht/client.js:1037:10)at Socket.emit (node:events:518:28)at UDP.onMessage [as onmessage] (node:dgram:942:8) {address: '195.8.44.196:20515'},Error: 203 missing 'token' keyat DHT._onResponseOrError (/project/workspace/node_modules/bittorrent-dht/client.js:1077:11)at DHT._onData (/project/workspace/node_modules/bittorrent-dht/client.js:1037:10)at Socket.emit (node:events:518:28)at UDP.onMessage [as onmessage] (node:dgram:942:8) {address: '5.79.173.86:26358'},Error: 202 Server Errorat DHT._onResponseOrError (/project/workspace/node_modules/bittorrent-dht/client.js:1077:11)at DHT._onData (/project/workspace/node_modules/bittorrent-dht/client.js:1037:10)at Socket.emit (node:events:518:28)at UDP.onMessage [as onmessage] (node:dgram:942:8) {address: '13.58.27.33:6881'},Error: 202 Server Errorat DHT._onResponseOrError (/project/workspace/node_modules/bittorrent-dht/client.js:1077:11)at DHT._onData (/project/workspace/node_modules/bittorrent-dht/client.js:1037:10)at Socket.emit (node:events:518:28)at UDP.onMessage [as onmessage] (node:dgram:942:8) {address: '18.223.137.220:6881'},Error: 203 missing 'token' keyat DHT._onResponseOrError (/project/workspace/node_modules/bittorrent-dht/client.js:1077:11)at DHT._onData (/project/workspace/node_modules/bittorrent-dht/client.js:1037:10)at Socket.emit (node:events:518:28)at UDP.onMessage [as onmessage] (node:dgram:942:8) {address: '103.15.254.90:18384'},Error: 203 missing 'token' keyat DHT._onResponseOrError (/project/workspace/node_modules/bittorrent-dht/client.js:1077:11)at DHT._onData (/project/workspace/node_modules/bittorrent-dht/client.js:1037:10)at Socket.emit (node:events:518:28)at UDP.onMessage [as onmessage] (node:dgram:942:8) {address: '189.147.128.165:6898'},Error: 203 missing 'token' keyat DHT._onResponseOrError (/project/workspace/node_modules/bittorrent-dht/client.js:1077:11)at DHT._onData (/project/workspace/node_modules/bittorrent-dht/client.js:1037:10)at Socket.emit (node:events:518:28)at UDP.onMessage [as onmessage] (node:dgram:942:8) {address: '49.187.241.178:6881'},Error: 203 missing 'token' keyat DHT._onResponseOrError (/project/workspace/node_modules/bittorrent-dht/client.js:1077:11)at DHT._onData (/project/workspace/node_modules/bittorrent-dht/client.js:1037:10)at Socket.emit (node:events:518:28)at UDP.onMessage [as onmessage] (node:dgram:942:8) {address: '195.211.192.65:43468'},Error: query timed outat Timeout.onTimeout [as _onTimeout] (/project/workspace/node_modules/bittorrent-dht/client.js:1445:8)at listOnTimeout (node:internal/timers:573:17)at process.processTimers (node:internal/timers:514:7) {address: '146.59.3.81:10240'},Error: query timed outat Timeout.onTimeout [as _onTimeout] (/project/workspace/node_modules/bittorrent-dht/client.js:1445:8)at listOnTimeout (node:internal/timers:573:17)at process.processTimers (node:internal/timers:514:7) {address: '195.8.44.196:41850'},Error: query timed outat Timeout.onTimeout [as _onTimeout] (/project/workspace/node_modules/bittorrent-dht/client.js:1445:8)at listOnTimeout (node:internal/timers:573:17)at process.processTimers (node:internal/timers:514:7) {address: '90.154.72.124:16183'},Error: query timed outat Timeout.onTimeout [as _onTimeout] (/project/workspace/node_modules/bittorrent-dht/client.js:1445:8)at listOnTimeout (node:internal/timers:573:17)at process.processTimers (node:internal/timers:514:7) {address: '193.233.120.141:16199'}
]
hash= <Buffer ba 16 0d 84 85 59 c6 05 52 b7 83 f2 3c d3 55 5b 2a 32 c7 b8>
DHT节点数组: [{id: '6b5611b3bfb7b8c8372c069a1e22e540609bda5a',addr: '87.98.162.88:6881'},{id: 'a811480f80e15cc2da5de2efb637b55db52124a1',addr: '178.32.217.211:6881'},{id: 'af15df9751f12d69492050218fbad8ec1245bce3',addr: '37.48.111.3:28010'},{id: '89f562f9f211a457acfe33b20f629ccac543ab72',addr: '51.38.34.56:51413'},{id: 'a995297613a597c36b910494795510f888a39263',addr: '89.149.202.214:21147'},{id: '9b334e8f333ef9fa3f2a52934ec1fd771535e68e',addr: '178.162.174.223:28009'},{id: 'a555d8b451608de54ccad3db9a7349ec5096e407',addr: '178.162.173.132:28015'},{id: 'a5d1090886585bfbec069777aed63730feb71483',addr: '188.34.190.92:6881'},{id: '85f1caa50b72c07a86a9501968002b7d59501a95',addr: '46.242.12.117:13066'},{id: '997e40df66b6fb64577807c6c30802851843520b',addr: '178.162.174.237:28001'},{id: 'ae5a71d29d9b373b84ff5d397db31135caea6af8',addr: '146.71.50.196:4000'},{id: 'a3a090845890ffa9883b2f65e4cdd319c536b0af',addr: '47.54.23.223:51413'},{id: '9aa0c85e94e179e4692e3d86e707d5899062bed8',addr: '178.162.173.166:28015'},{id: '9f53fe555940af320f9c7540445589cc84369884',addr: '23.158.56.119:10095'},{id: '84fe47cc3c76ba793bc0f7f1ae6996431161ba60',addr: '88.201.206.119:3334'},{id: 'a3573c0b6206eaa41f2ce9d29dc9de5fef2865d1',addr: '81.207.238.6:1024'},{id: '9191c3ea0cf55e663d18fc552df2a580395f7a80',addr: '82.65.220.127:6885'},{id: 'b2498f2a643a78207bcb3dce4307f8e0a615297e',addr: '24.20.153.108:6969'},{id: 'b5a304f2843ba084416908fe7e7002fe9c5478ad',addr: '14.48.222.76:7801'},{id: 'a449d2b1d34c39c9ee4d917b24ce22af0db22248',addr: '5.129.239.125:49001'},{id: '9900258a640be2f0cd0c35a961a386c1cc9cf589',addr: '103.140.3.17:15195'},{id: 'e3d5f4b4aed69802dbb34b24f6fd358063dd1cf4',addr: '14.104.200.224:10920'},{id: 'e0f0da00d1bee565e0a21088787bdcbb4a2633e7',addr: '125.77.176.152:37740'},{id: 'e0dd29c1e17a46839ab2e54c32675e5463f9e03b',addr: '113.0.38.69:32981'},{id: 'e36ff0fa563342fd2fc905576a784e4277b9ee59',addr: '124.115.94.28:64425'},{id: 'e13b76f9a31ebe5a6d29b765b293143d461ca426',addr: '122.192.133.176:6887'},{id: 'e29a50d52c1c9029a9d3a7f9fe152e5803ae3344',addr: '36.4.10.196:38999'},{id: 'e5c14f6d2ee91906c9265afaab45482594f7467e',addr: '221.157.193.11:6881'},{id: 'e46507c044a8265a16360dc481a3c631dc79aff1',addr: '61.147.218.204:56016'},{id: 'e66a2f0d5056c00ba4425bab7ca0cfab4df7ddcf',addr: '120.40.56.40:22840'},{id: 'e632ccac3c4bc229581ea4d2039626cb12881e1c',addr: '218.91.170.101:6891'},{id: 'e6b14eb0462c8ac5e7553ec6b6003a65075c363e',addr: '39.112.211.158:40954'},{id: 'e71f1918956034be0a634b9cdbbec5665d2ac654',addr: '183.134.38.41:6890'},{id: 'e73c15b0beffd4373c145e00f07d4a450408dd52',addr: '61.173.61.203:59951'},{id: 'e711e8dda372363d608afa0a050d3fbdec8425db',addr: '185.149.91.43:51079'},{id: 'e71361d74d4799186ac0d62bccecbfe66c63b2cd',addr: '169.150.223.225:6881'},{id: 'e706b08a6b939e8d5e37ea2e4f9418c97cc1d66d',addr: '94.75.194.118:28006'},{id: 'e703e8c3a3c766f1ed6a365998f05ba84d81dd83',addr: '77.82.189.152:49001'},{id: 'e72eb05590c079a0acb407fdbb5204bc9b95d53b',addr: '49.245.2.242:11706'},{id: 'e725c22b82a5adebf493bd66ac859562b4f2b3ee',addr: '14.103.76.31:60020'},{id: 'e71f2ba6bec45aff6d35b3356e1ed3df4d5f15bb',addr: '121.236.15.238:9888'},{id: 'e751aa9bd98fbfc9b3aaa026443ebd8c429f5fcc',addr: '195.154.171.138:5250'},{id: 'e74e28a755e0b680d4197753c31e9ad081d8b8b4',addr: '184.64.199.181:48670'},{id: 'e74ca9d20211352647212795b0c4e761c067d874',addr: '220.200.42.0:62559'},{id: 'e74bbc1b073c79c49051b88c502986c8c21dd36f',addr: '81.40.181.46:8621'},{id: 'e7507c39a1cb087aa7ccc8f3adc3d04203d37f31',addr: '185.203.56.10:60696'},{id: 'e743d8ec35a30db1bb12137f67eae6e39b013508',addr: '183.195.21.211:13284'},{id: 'e74f8c5f47304ac841de681ec4b4cfbc134cbd7f',addr: '112.24.228.151:27868'},{id: 'e7503d0d29de824e4caae6017a4e6419d1a43082',addr: '81.202.75.215:6889'},{id: 'e753d24bdf60c8128dd48d15431093c20c34693d',addr: '209.193.67.78:51413'},{id: 'e755e97111a41814b466e4b7a5526a06f2cc534c',addr: '186.57.159.109:6881'},{id: 'e761c3d8d84b09395c0a2affd38e24c0c9418b4c',addr: '42.193.175.166:18826'},{id: 'e760ac2c0424839b85bf02179b32866ab297689b',addr: '88.0.223.106:51413'},{id: 'e767d67550eb627914a8d20b78728c9d59d95fc5',addr: '195.230.109.6:43242'},{id: 'e760a6ddaf17ee5725d15c21d9ed8186085ff04c',addr: '59.5.76.86:41202'},{id: 'e76579760dc1ad7e3d992d2bef992d6ea156bd89',addr: '131.153.18.45:22574'},{id: 'e762cffedfef8b697f64e88452b548e4ca052d0f',addr: '217.225.89.57:6881'},{id: 'e763d907a08cf6fcb07eecc265d595632377624c',addr: '107.181.234.142:51413'},{id: 'e766eb1feb72f2cb87aa747086e3faef8764f06e',addr: '219.251.137.25:40972'},{id: 'e760b549f1f1bbe9ebb3a6db3c870c3e99245e52',addr: '59.6.5.153:64536'},{id: 'e760afea3b0f54c53a75070241592c5dc6fd0c59',addr: '27.125.248.23:15630'},{id: 'e767644b57a9dbe2029ead2d395633301bfff07e',addr: '59.3.119.55:40804'},{id: 'e763054ba30fa0e3e767721c6a911ce9e7ac50fe',addr: '60.163.61.30:17077'},{id: 'e76595f27f020daa5d106a3d4e25bd24b1564b6a',addr: '179.131.236.32:50321'},{id: 'e7672ee9801a6f85413c6314f6305f7b94f93e37',addr: '121.128.111.23:40811'},{id: 'e760245fb585019447ba47e497a94d662e6349c8',addr: '59.188.57.119:6889'},{id: 'e7620cc83f2240a3b3d69ef294eece88c729f902',addr: '72.21.17.91:61318'},{id: 'e761f830ff2f48422470dabafe53ce7d08fb3cd2',addr: '85.140.162.247:55414'},{id: 'e7672049fe578e109d980c8eb64057cdb3454c02',addr: '207.65.155.118:51413'},{id: 'e76793864960d52d457ef2ebe2fcb40d02f57ca9',addr: '157.157.193.103:5030'},{id: 'e769bdb7be648414ae3cafb158fb4c18fa94826a',addr: '72.21.17.3:12743'},{id: 'e76a5736ae4740254fc6c57c04afe7bcd093c5fc',addr: '222.113.206.3:32996'},{id: 'e769bd26374a11ebcd96ac0021ea2bbe5aec00f2',addr: '72.21.17.3:57563'},{id: 'e76b41a618e160c3a5c837d5ecc70648305e58a4',addr: '2.98.139.134:19856'},{id: 'e768b641d52f2dfe3bf304d3ee8a8ce4dded3bf8',addr: '119.197.185.173:60761'},{id: 'e76bb57181f76165df4289688ea4e6730f3cf7ec',addr: '188.21.206.210:29115'},{id: 'e76b7ad6ae529049f1f1bbe9ebb3a6db3c870ce1',addr: '84.9.37.4:35609'},{id: 'e76bc6b2e88753a773e28c5c73ce4dd13178d9c6',addr: '111.196.228.198:5774'},{id: 'e769bf4d3366684d43e979222cd64fdb976a0142',addr: '72.21.17.3:62436'},{id: 'e7683854beae9b503af99d95f65dc8626d274b94',addr: '178.162.174.237:28007'},{id: 'e7686b42c80418f1dbaac7420de4e7b865eed412',addr: '89.105.15.4:16345'},{id: 'e769beafcbe43b71166602d4f70a330402c7b07a',addr: '72.21.17.3:20057'},{id: 'e7680d81a626489edee91255d9a08264a78cb8f7',addr: '170.150.1.175:26340'},{id: 'e76c07b8216f99cb4eee48795bc504be5e85ad92',addr: '178.221.133.198:46873'},{id: 'e76c04cffe2e06e0aa57a0edec0062b8f42c5803',addr: '49.117.14.143:8392'},{id: 'e76c0d1dc74193c186b35361733df3bb591d16ef',addr: '46.197.49.135:26616'},{id: 'e76c00c84cc41bd997f5c942055eed780a74189e',addr: '185.251.151.248:51413'},{id: 'e76c047b5dc0aaad6529b9e9487159c2c74fed01',addr: '223.247.106.145:55291'},{id: 'e76c00169995fb98f8c90f59817b3c989b5dfb3d',addr: '82.132.246.194:54677'},{id: 'e76c074d30740c88fcdac1f93ba8faa46fa572a5',addr: '122.148.246.194:30754'},{id: 'e76c0f976890ad222b72782990615e613e03d7e7',addr: '212.35.183.80:3168'},{id: 'e76c06ba160fdc583ed1969d1db70fae30a78541',addr: '90.15.84.33:40622'},{id: 'e76c06ba160fdc583ed1969d1db70f8dc9547307',addr: '49.12.86.202:6883'},{id: 'e76c06ba160fdc583ed152fbdd8d2da45f042476',addr: '146.19.24.245:23544'},{id: 'e76c0993a20cef635b7fad5e4dc95a8bcae8e107',addr: '132.147.119.80:1041'},{id: 'e76c0df644d2255ca4a4c8c8ee3029ecb2fd2d1b',addr: '178.234.59.239:6881'},{id: 'e76c1edc0b17287dbee23dbdc40875faab321fff',addr: '93.156.208.226:48379'},{id: 'e76c1fd6f2a275af36e9955fbcce091188938046',addr: '200.121.141.155:31719'},{id: 'e76c108b98fc86ac2e0d75ed5ff5c03e163dd9eb',addr: '82.7.191.13:36957'},{id: 'e76c1bde86a78072e5aafa52b4d87b270f1d571e',addr: '70.175.203.76:6881'},... 100 more items
]

3. 发布announce

dht.announce(sha, config.dht.announce, function (err) {if (err !== null) {console.log('Announced ' + sha)}
})
  • dht.announce(sha, ...):将特定的 SHA 值(GitTorrent项目即 Git 提交的哈希)及其相应的网络地址发布到 DHT 网络。其他节点可以通过这个信息来查找特定的 Git 数据。通过这个函数,节点通知 DHT 网络,它正在下载一个特定的种子。这使得其他节点能够发现这个下载中的节点,从而进行数据交换。具体实现如下:
/*** Announce that the peer, controlling the querying node, is downloading a torrent on a port.* 声明当前节点正在下载一个种子,告诉DHT网络此信息。* * @param {string|Buffer} infoHash - 种子的 infoHash,唯一标识符。* @param {number} port - 当前节点下载种子的端口号。* @param {function=} cb - 可选的回调函数,宣布完成时调用。*/
DHT.prototype.announce = function (infoHash, port, cb) {var self = this// 如果回调函数没有提供,设置一个空函数if (!cb) cb = function () {}// 如果DHT实例已经销毁,调用回调函数并传递错误信息if (self.destroyed) return cb(new Error('dht is destroyed'))// 将infoHash转换为Buffer格式infoHash = idToBuffer(infoHash)// 将infoHash转换为十六进制字符串,便于处理和输出var infoHashHex = idToHexString(infoHash)// 输出调试信息,表示正在宣布下载self._debug('announce %s %s', infoHashHex, port)// 遍历本地节点地址,为每个地址添加peer信息到DHT表中self.localAddresses.forEach(function (address) {self._addPeer(address + ':' + port, infoHashHex)})// 查找是否已经有对应 infoHash 的路由表存在var table = self.tables[infoHashHex]if (table) {// 如果有对应的路由表,直接使用表中的最近节点进行处理onClosest(null, table.closest({ id: infoHash }, K))} else {// 否则,发起一个lookup查询,查找最近的节点self.lookup(infoHash, onClosest)}/*** 当查找到最近的节点时调用的回调函数* @param {Error|null} err - 错误信息,如果存在错误* @param {Array} closest - 查找到的最近节点列表*/function onClosest (err, closest) {if (err) return cb(err) // 如果有错误,调用回调并返回错误信息// 遍历每个最近的节点,向他们发送 "announce_peer" 消息closest.forEach(function (contact) {self._sendAnnouncePeer(contact.addr, infoHash, port, contact.token)})// 输出调试信息,表示宣布操作结束self._debug('announce end %s %s', infoHashHex, port)// 调用回调函数,表示宣布完成cb(null)}
}
  • 路由表的重用
    • 如果 dht.announce 在调用 dht.lookup 之后的 5 分钟内被调用,并且路由表仍然有效,则可以重用最近的路由表。这意味着函数会更快地完成,因为不需要再次查询其他节点。
    • 如果调用时没有缓存的路由表,dht.announce 会首先执行 dht.lookup 来发现相关节点。这将耗费更多时间,因为需要重新获取有效的 “tokens”。下面是一个官方提供的路由表示例:
const dht1 = new DHT()// some time passes ...// destroy the dht
const nodes = dht1.toJSON().nodes
dht1.destroy()// some time passes ...// initialize a new dht with the same routing table as the first
const dht2 = new DHT()nodes.forEach(function (node) {dht2.addNode(node)
})

4. 增dht.put(opts, callback)

import DHT from 'bittorrent-dht'
const dht = new DHT()
const value = Buffer.alloc(200).fill('abc')// 可放入一个小于 1000 字节的值dht.put({ v: value }, function (err, hash) {console.error('error=', err)console.log('hash=', hash)
})
  • 此扩展允许在 BitTorrent DHT 中存储和检索任意数据 [1]。它既支持存储不可变项目(其中密钥是数据本身的 SHA-1 哈希值),也支持存储可变项目(其中密钥是用于对数据进行签名的密钥对的公钥)。具体实现如下:
/*** Write arbitrary mutable and immutable data to the DHT.* Specified in BEP44: http://bittorrent.org/beps/bep_0044.html* @param {Object} opts* @param {function=} cb*/
DHT.prototype.put = function (opts, cb) {var self = thisvar isMutable = opts.k || opts.sigif (opts.v === undefined) {throw new Error('opts.v not given')}if (opts.v.length >= 1000) {throw new Error('v must be less than 1000 bytes in put()')}if (isMutable && opts.cas && typeof opts.cas !== 'number') {throw new Error('opts.cas must be an integer if provided')}if (isMutable && !opts.k) {throw new Error('opts.k ed25519 public key required for mutable put')}if (isMutable && opts.k.length !== 32) {throw new Error('opts.k ed25519 public key must be 32 bytes')}if (isMutable && !opts.sig) {throw new Error('opts.sig signature required for mutable put')}if (isMutable && opts.sig.length !== 64) {throw new Error('opts.sig signature must be 64 bytes')}if (isMutable && opts.salt && opts.salt.length > 64) {throw new Error('opts.salt is > 64 bytes long')}if (isMutable && opts.seq === undefined) {throw new Error('opts.seq not provided for a mutable update')}if (isMutable && typeof opts.seq !== 'number') {throw new Error('opts.seq not an integer')}return self._put(opts, cb)
}/*** put() without type checks for internal use* @param {Object} opts* @param {function=} cb*/
DHT.prototype._put = function (opts, cb) {var self = thisvar pending = 0var errors = []var isMutable = opts.k || opts.sigvar hash = isMutable? sha1(opts.salt ? Buffer.concat([ opts.salt, opts.k ]) : opts.k): sha1(opts.v)if (self.nodes.toArray().length === 0) {process.nextTick(function () {addLocal(null, [])})} else {self.lookup(hash, onLookup)}function onLookup (err, nodes) {if (err) return cb(err)nodes.forEach(function (node) {put(node)})addLocal()}function addLocal () {var localData = {id: self.nodeId,v: opts.v}var localAddr = '127.0.0.1:' + self._portif (isMutable) {if (opts.cas) localData.cas = opts.caslocalData.sig = opts.siglocalData.k = opts.klocalData.seq = opts.seqlocalData.token = opts.token || self._generateToken(localAddr)}self.nodes.add({id: hash,addr: localAddr,data: localData})if (pending === 0) {process.nextTick(function () { cb(errors, hash) })}}return hashfunction put (node) {if (node.data) return // skip data nodespending += 1var t = self._getTransactionId(node.addr, next(node))var data = {a: {id: opts.id || self.nodeId,v: opts.v},t: transactionIdToBuffer(t),y: 'q',q: 'put'}if (isMutable) {data.a.token = opts.token || self._generateToken(node.addr)data.a.seq = Math.round(opts.seq)data.a.sig = opts.sigdata.a.k = opts.kif (opts.salt) data.a.salt = opts.saltif (opts.cas) data.a.cas = Math.round(opts.cas)}self._send(node.addr, data)}function next (node) {return function (err) {if (err) {err.address = node.addrerrors.push(err)}if (--pending === 0) cb(errors, hash)}}
}

5. 查dht.get(hash, opts, callback)

    var val = new Buffer(key, 'hex')dht.get(val, function (err, res) {if (err) {return console.error(err)}var json = res.v.toString()var repos = JSON.parse(json)}

GitTorrent的gittorrentd命令实现

  • gittorrentd通过 BitTorrent DHT 和 GitTorrent 协议,将 Git 仓库数据通过 P2P 网络共享。
  • 通过遍历本地的 Git 仓库目录,列出所有引用,并将其 SHA 通过 DHT 网络广播。
  • 使用 net 模块创建 TCP 服务器,响应来自 P2P 节点的 Git 数据请求,并通过 WebTorrent 实现种子文件的共享。

创建 DHT 实例

#!/usr/bin/env node  // 文件开头的这行叫做“shebang”,它告诉操作系统使用node来运行这个脚本。因此,当你在命令行中运行./gittorrentd时,它实际上是在用Node.js环境执行该脚本。// 引入所需的模块
var DHT = require('bittorrent-dht'); // BitTorrent 的 DHT 模块,用于发现对等节点
var EC = require('elliptic').ec; // 椭圆曲线加密库,支持多种加密算法
var ed25519 = new EC('ed25519'); // 使用 Ed25519 椭圆曲线进行加密操作
var exec = require('child_process').exec; // 允许执行 shell 命令
var glob = require('glob'); // 用于文件路径匹配
var fs = require('fs'); // 文件系统模块
var hat = require('hat'); // 随机 ID 生成库
var net = require('net'); // 提供网络套接字操作
var Protocol = require('bittorrent-protocol'); // BitTorrent 协议模块
var spawn = require('child_process').spawn; // 生成子进程以运行外部命令
var ut_gittorrent = require('ut_gittorrent'); // 用于 BitTorrent 上的 Git 传输扩展
var ut_metadata = require('ut_metadata'); // BitTorrent 元数据传输扩展
var WebTorrent = require('webtorrent'); // WebTorrent 客户端库,用于 BitTorrent 下载和种子
var zeroFill = require('zero-fill'); // 补零函数,格式化数字
var config = require('./config'); // 加载用户自定义配置
var git = require('./git'); // 加载 Git 操作相关的自定义模块// 生成 BitTorrent 客户端的版本号字符串(用于 Peer ID),根据 package.json 的版本生成
var VERSION = require('./package.json').version.match(/([0-9]+)/g).slice(0, 2).map(zeroFill(2)).join('');// 错误处理函数,打印错误并退出
function die(error) {console.error(error);process.exit(1);
}// 创建 DHT 实例,使用配置文件中定义的引导节点
var dht = new DHT({bootstrap: config.dht.bootstrap
});
// DHT 监听来自 Peers 的请求
dht.listen(config.dht.listen);var announcedRefs = {}; // 存储已经公布的 Git 引用 (refs)
var userProfile = {repositories: {} // 存储用户的 Git 仓库信息
};// 生成或读取密钥文件
var key = create_or_read_keyfile();function create_or_read_keyfile() {// 如果密钥文件不存在,创建一个新的 Ed25519 密钥对if (!fs.existsSync(config.key)) {var keypair = new EC('ed25519').genKeyPair();fs.writeFileSync(config.key, JSON.stringify({pub: keypair.getPublic('hex'), // 公钥priv: keypair.getPrivate('hex') // 私钥}));}// 读取已存在的密钥文件var key = JSON.parse(fs.readFileSync(config.key).toString());return ed25519.keyPair({priv: key.priv, // 私钥privEnc: 'hex',pub: key.pub, // 公钥pubEnc: 'hex'});
}// 补零函数,确保缓冲区长度达到指定的字节数
function bpad(n, buf) {if (buf.length === n) return buf;if (buf.length < n) {var b = new Buffer(n);buf.copy(b, n - buf.length);for (var i = 0; i < n - buf.length; i++) b[i] = 0;return b;}
}

当 DHT 就绪时,执行以下操作


var head = ''; // 当前仓库的 HEAD 引用// 当 DHT 就绪时,执行以下操作
dht.on('ready', function () {// 查找文件目录// 使用 glob 搜索所有带有 git-daemon-export-ok 文件的目录(Git 可公开的仓库)//,比如/projectA/git-daemon-export-ok和/projectB/.git/git-daemon-export-okvar repos = glob.sync('*/{,.git/}git-daemon-export-ok', { strict: false });var count = repos.length;repos.forEach(function (repo) {console.log('in repo ' + repo);repo = repo.replace(/git-daemon-export-ok$/, ''); // 文件名中移除 git-daemon-export-ok console.log(repo);var reponame = repo.replace(/\/.git\/$/, ''); // 提取仓库名或路径部分userProfile.repositories[reponame] = {}; // 初始化用户的仓库信息// 使用自定义 Git 模块列出该仓库的所有引用 (refs)// 第二个参数是一个回调函数,当 git ls-remote 获取到每一个引用时,都会调用该回调函数,并传递该引用的 sha(40 位提交哈希)和 ref(引用名,通常是分支或标签的全名)var ls = git.ls(repo, function (sha, ref) {// 只处理 HEAD 和 heads(分支)引用,忽略其他引用if (ref !== 'HEAD' && !ref.match(/^refs\/heads\//)) {return;}if (ref === 'refs/heads/master') {head = sha; // 如果是 master 分支,将其 SHA 设为 HEAD}userProfile.repositories[reponame][ref] = sha; // 记录仓库引用if (!announcedRefs[sha]) {console.log('Announcing ' + sha + ' for ' + ref + ' on repo ' + repo);announcedRefs[sha] = repo; // 标记已公布的引用dht.announce(sha, config.dht.announce, function (err) {if (err !== null) {console.log('Announced ' + sha);}});}});// 当列出引用结束时,减少计数器,最终发布用户的可变密钥ls.stdout.on('end', function () {count--;if (count <= 0) {publish_mutable_key(); // 当所有仓库处理完毕,发布密钥}});ls.on('exit', function (err) {if (err) {die(err); // 如果 git.ls 失败,终止程序}});});// 定义publish_mutable_key函数发布用户的可变密钥到 DHTfunction publish_mutable_key() {var json = JSON.stringify(userProfile); // 将用户仓库信息序列化为 JSONif (json.length > 950) {console.error("Can't publish mutable key: doesn't fit in 950 bytes.");return false; // 如果数据过大,无法发布}var value = new Buffer(json.length);value.write(json);var sig = key.sign(value); // 使用用户密钥对数据进行签名var opts = {k: bpad(32, Buffer(key.getPublic().x.toArray())), // 公钥seq: 0,v: value, // 用户数据sig: Buffer.concat([bpad(32, Buffer(sig.r.toArray())), // 签名的一部分bpad(32, Buffer(sig.s.toArray()))  // 签名的另一部分])};console.log(json);// 将可变密钥发布到 DHTdht.put(opts, function (errors, hash) {console.error('errors=', errors);console.log('hash=', hash.toString('hex')); // 打印发布的哈希值});}// 创建一个 TCP 服务器,并处理 P2P 连接net.createServer(function (socket) {var wire = new Protocol(); // 创建新的 BitTorrent 协议实例(bittorrent-protocol)wire.use(ut_gittorrent()); // 使用 GitTorrent 扩展wire.use(ut_metadata()); // 使用元数据传输扩展// 将 socket 和协议 wire 连接起来socket.pipe(wire).pipe(socket);// 当收到握手时,处理对等节点wire.on('handshake', function (infoHash, peerId) {console.log('Received handshake for ' + infoHash.toString('hex'));var myPeerId = new Buffer('-WW' + VERSION + '-' + hat(48), 'utf8');wire.handshake(new Buffer(infoHash), new Buffer(myPeerId)); // 发送握手响应});// 当对等节点请求 Git 包时,生成相应的数据wire.ut_gittorrent.on('generatePack', function (sha) {console.error('calling git pack-objects for ' + sha);if (!announcedRefs[sha]) {console.error('Asked for an unknown sha: ' + sha);return;}var directory = announcedRefs[sha];var have = null;if (sha !== head) {have = head;}// 使用 Git 打包对象var pack = git.upload_pack(directory, sha, have);pack.stderr.pipe(process.stderr);// 当打包完成时,写入到文件pack.on('ready', function () {var filename = sha + '.pack';var stream = fs.createWriteStream(filename);pack.stdout.pipe(stream);stream.on('close', function () {console.error('Finished writing ' + filename);// 使用 WebTorrent 将文件进行种子共享var webtorrent = new WebTorrent({dht: {bootstrap: config.dht.bootstrap},tracker: false});// 种子创建完成后发送给对等节点webtorrent.seed(filename, function onTorrent (torrent) {console.error(torrent.infoHash);wire.ut_gittorrent.sendTorrent(torrent.infoHash);});});});// 打包进程结束时,检查退出码pack.on('exit', function (code) {if (code !== 0) {console.error('git-upload-pack process exited with code ' + code);}});});}).listen(config.dht.announce); // 监听配置中定义的端口
});

相关文章:

【p2p、分布式,区块链笔记 Torrent】: WebTorrent GitTorrent bittorrent-dht

bittorrent-dht模块 BitTorrent DHT 通过 DHT 网络广播值&#xff0c;允许其他用户通过 DHT 来发现和获取这些数据。 1. 导入依赖 var DHT require(bittorrent-dht)2. 创建实例 var dht new DHT({bootstrap: config.dht.bootstrap }) dht.listen(config.dht.listen)new D…...

【Next.js 项目实战系列】05-删除 Issue

原文链接 CSDN 的排版/样式可能有问题&#xff0c;去我的博客查看原文系列吧&#xff0c;觉得有用的话&#xff0c;给我的库点个star&#xff0c;关注一下吧 上一篇【Next.js 项目实战系列】04-修改 Issue 删除 Issue 添加删除 Button​ 本节代码链接 这里我们主要关注布局…...

Springboot api http并发测试请求

pom.xml <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId> </dependency> 线程发起请求 package com.example.demo;import org.springframework.http.HttpEntity; import org…...

Qt的websocket客户端和服务器测试(非安全版本)

测试内容&#xff1a; 客户端&#xff1a; 1 连接服务器 2 发送数据 3 处理错误信号 4 监听断开信号 5 接收服务器的数据 服务器&#xff1a; 1 监听等待客户端连接 2 向指定的客户端发送数据 4 监听断开信号 5 接收客户端的数据 测试界面 工程文件.pro添加的内容&#xff1a;…...

Prometheus运维监控平台之监控指标注册到consul脚本开发、自定义监控项采集配置调试(三)

系列文章目录 运维监控平台搭建 运维监控平台监控标签 golang_Consul代码实现Prometheus监控目标的注册以及动态发现与配置V1版本 文章目录 系列文章目录目的一、监控指标注册到consul的golang脚本开发1、修改settings.yaml文件2、修改config/ocnsul,go文件3、修改core/consul…...

C语言——数组

1.数组的概念 数组是一组相同类型元素的集合&#xff1b; 数组中可以存放1个或多个元素&#xff0c;但数组元素个数不能为0。 同时数组可以分为一维数组和多维数组&#xff0c;多维数组一般常见 是二维数组。 2.一维数组的创建和初始化 一维数组的创建的基本语法&#xff1a; …...

MySQL-09.DDL-表结构操作-查询修改删除

一.查询 二.修改 三.删除 -- DDL&#xff1a;查看表结构 -- 查看&#xff1a;当前数据库下的表show tables ;-- 查看&#xff1a;查看指定的表结构 desc tb_emp;-- 查看&#xff1a;数据库的建表语句 show create table tb_emp;-- DDL:修改表结构 -- 修改&#xff1a;为表tb…...

WileyNJDv5_Template模板无法编译生成pdf文件

文章目录 问题解决办法结果 问题 使用WileyNJDv5_Template模板时&#xff0c;修改tex文件里的内容&#xff0c;按F6编译后&#xff0c;并没有报错&#xff0c;但是编辑后的pdf文件没有生成&#xff0c;因为看文件夹里的pdf文件日期还是以前的日期&#xff0c;所以是旧版的pdf文…...

亿配芯城(ICGOODFIND)教你外贸(海外)推广电子元器件芯片的专用词语

在电子元器件行业&#xff0c;海外推广是企业拓展市场、提升竞争力的重要手段。而在海外推广过程中&#xff0c;恰当运用专用词语能够准确传达产品信息、吸引客户关注&#xff0c;提升推广效果。本文将详细介绍亿配芯城&#xff08;ICGOODFIND&#xff09;电子元器件海外推广中…...

windows和linux的一些使用问题一一记录

文章目录 windows 11 激活wsl文件共享命令互通wslg网络 Hyper-V双系统遇到再记录……… windows 11 激活 然后执行 slmgr /skms kms.03k.org slmgr /atowsl 卡死打开任务管理关闭下就行了 wsl --list -v # 安装的 wsl --list --online #可以安装的wsl -d kali-linux # 启…...

排序算法上——插入,希尔,选择,堆排序

前言&#xff1a; 常见排序方法如下&#xff1a; 本篇将介绍4种排序方法&#xff0c;分别为插入排序&#xff0c;希尔排序&#xff0c;选择排序&#xff0c;堆排序&#xff0c;并分别举例与讲解。 一. 插入排序 1.1 含义与动图分析 插入排序的思想是在有序区间的下一个位置…...

Mycat 详细介绍及入门实战,解决数据库性能问题

一、基本原理 1、数据分片 &#xff08;1&#xff09;、水平分片 Mycat 将一个大表的数据按照一定的规则拆分成多个小表&#xff0c;分布在不同的数据库节点上。例如&#xff0c;可以根据某个字段的值进行哈希取模&#xff0c;将数据均匀的分布到不同的节点上。 这样做的好处…...

FFmpeg源码:avformat_new_stream函数分析

一、avformat_new_stream函数的声明 avformat_new_stream函数定义在FFmpeg源码&#xff08;本文演示用的FFmpeg源码版本为7.0.1&#xff09;的头文件libavformat/avformat.h中&#xff1a; /*** Add a new stream to a media file.** When demuxing, it is called by the dem…...

【java】深入解析Lambda表达式

Lambda表达式是Java 8引入的一项重要特性&#xff0c;它提供了一种简洁的方式来实现函数式编程。Lambda表达式的使用广泛而且灵活&#xff0c;可以简化代码并提高可读性。本篇文章将深入解析Lambda表达式&#xff0c;包括使用场景、基础学习、代码案例、实现方法和注意事项等方…...

Chromium html<img>对应c++接口定义

<img src"tulip.jpg" alt"上海鲜花港 - 郁金香" /> 1、html_tag_names.json5中接口定义&#xff1a; &#xff08;third_party\blink\renderer\core\html\html_tag_names.json5&#xff09; {name: "img",constructorNeedsCreateElementF…...

卸载Python

1、查看安装框架位置并删除 Sudo rm -rf /Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8 2、查看应用并删除 在 /Applications/Python 3.x 看是否存在&#xff0c;如果存在并删除。 3、删除软连接 ls -l /usr/bin/py* 或 ls -…...

算法剖析:二分查找

文章目录 前言二分查找模板朴素模板左右查找模板 一、二分查找二、 在排序数组中查找元素的第一个和最后一个位置三、搜索插入位置四、x 的平方根五、山脉数组的峰顶索引六、寻找峰值七、寻找旋转排序数组中的最小值八、 点名总结 前言 二分查找是一种高效的查找算法&#xff…...

Invoke 和 InvokeRequired以及他们两个的区别

在.NET中&#xff0c;Invoke和InvokeRequired是Windows Forms编程中用于确保线程安全的关键方法和属性。它们通常用在多线程环境中&#xff0c;以确保UI控件的更新操作在创建控件的线程上执行&#xff0c;避免因跨线程操作导致的异常。 InvokeRequired 属性 InvokeRequired属…...

SpringBoot概览及核心原理

Spring Boot 是由Pivotal 团队设计的全新框架&#xff0c;其目的是用来简化 Spring 应用开发过程。该框架使用了特定的方式来进行配置&#xff0c;从而使得开发人员不再需要定义一系列样板化的配置文件&#xff0c;而专注于核心业务开发&#xff0c;项目涉及的一些基础设施则交…...

根据basic auth请求https获取token

根据basic auth请求https获取token 对接第三方接口&#xff0c;给了接口文档&#xff0c;但是没有示例代码&#xff0c;postman一直可请求成功&#xff0c;java就是不行。百思不得其解&#xff0c;最后请求公司大神&#xff0c;得到一套秘籍。 第一步 第二步 Authorization&am…...

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

Leetcode 3576. Transform Array to All Equal Elements

Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接&#xff1a;3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到&#xf…...

golang循环变量捕获问题​​

在 Go 语言中&#xff0c;当在循环中启动协程&#xff08;goroutine&#xff09;时&#xff0c;如果在协程闭包中直接引用循环变量&#xff0c;可能会遇到一个常见的陷阱 - ​​循环变量捕获问题​​。让我详细解释一下&#xff1a; 问题背景 看这个代码片段&#xff1a; fo…...

【WiFi帧结构】

文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成&#xff1a;MAC头部frame bodyFCS&#xff0c;其中MAC是固定格式的&#xff0c;frame body是可变长度。 MAC头部有frame control&#xff0c;duration&#xff0c;address1&#xff0c;address2&#xff0c;addre…...

CMake基础:构建流程详解

目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

家政维修平台实战20:权限设计

目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系&#xff0c;主要是分成几个表&#xff0c;用户表我们是记录用户的基础信息&#xff0c;包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题&#xff0c;不同的角色&#xf…...

srs linux

下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935&#xff0c;SRS管理页面端口是8080&#xff0c;可…...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...