【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 网络广播值,允许其他用户通过 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 的排版/样式可能有问题,去我的博客查看原文系列吧,觉得有用的话,给我的库点个star,关注一下吧 上一篇【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客户端和服务器测试(非安全版本)
测试内容: 客户端: 1 连接服务器 2 发送数据 3 处理错误信号 4 监听断开信号 5 接收服务器的数据 服务器: 1 监听等待客户端连接 2 向指定的客户端发送数据 4 监听断开信号 5 接收客户端的数据 测试界面 工程文件.pro添加的内容:…...

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

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

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

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

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

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 # 启…...

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

Mycat 详细介绍及入门实战,解决数据库性能问题
一、基本原理 1、数据分片 (1)、水平分片 Mycat 将一个大表的数据按照一定的规则拆分成多个小表,分布在不同的数据库节点上。例如,可以根据某个字段的值进行哈希取模,将数据均匀的分布到不同的节点上。 这样做的好处…...

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

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

Chromium html<img>对应c++接口定义
<img src"tulip.jpg" alt"上海鲜花港 - 郁金香" /> 1、html_tag_names.json5中接口定义: (third_party\blink\renderer\core\html\html_tag_names.json5) {name: "img",constructorNeedsCreateElementF…...

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

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

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

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

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

【基础版】React缓存路由
前言 项目背景 Reactumireact-router5 需求 用户在某一页面操作后点击跳转到其详情页,返回到列表页还是之前操作过的页面,即把页面缓存下来(基础版先处理路由缓存,tab页展示先不处理) 实践 在布局页面对页面进行…...

Java基础15-Java高级
十五、Java高级 单元测试、反射、注解、动态代理。 1、单元测试 定义:就是针对最小的功能单元(方法),编写测试代码对其进行正确性测试。 1.1 Junit单元测试框架 可以用来对方 法进行测试,它是第三方公司开源出来的(很多开发工具已经集成了Junit框架&…...

selenium工具的几种截屏方法介绍(9)
在使用selenium做自动化的时候,可以对于某些场景截图保存当时的执行情况,方便后续定位问题或者作为一些证据保留现场。 获取元素后将元素截屏 我们获取元素后,使用函数screenshot将元素截屏,参数filename传入完整的png文件名路径…...

【设计模式】深入理解Python中的过滤器模式(Filter Pattern)
深入理解Python中的过滤器模式(Filter Pattern) 在软件设计中,面对复杂的数据处理需求时,我们常常需要从一组数据中筛选出符合特定条件的子集。**过滤器模式(Filter Pattern)**是一种能够简化这种操作的设…...

vue的动态组件 keep-alive
1. 什么是动态组件 动态组件指的是 动态切换组件的显示与隐藏 2. 如何实现动态组件渲染 vue提供了一个内置的<component>组件,专门用来实现动态组件的渲染。 作用:组件的占位符is的值表示要渲染的组件 示例代码如下: Left.vue的代…...

现代框架开发官网
一、项目背景 维护过 灵犀官网、企业邮官网、免费邮官网 均使用 jquery webpack多页面打包的方式 开发起来较为繁琐 新的官网项目,想使用现代前端框架,但SPA应用不利于SEO 使用SSR方案又依赖运维,增加维护和沟通成本 二、SSG vs 预渲染 S…...

一篇文章快速认识YOLO11 | 关键改进点 | 安装使用 | 模型训练和推理
前言 本文分享YOLO11的关键改进点、性能对比、安装使用、模型训练和推理等内容。 YOLO11 是 Ultralytics 最新的实时目标检测器,凭借更高的精度、速度和效率重新定义了可能性。 除了传统的目标检测外,YOLO11 还支持目标跟踪、实例分割、姿态估计、OBB…...

AtCoder Beginner Contest 375(A,B,C,D,E,F)(大模拟,前缀和,dp,离线处理,Floyd)
比赛链接 AtCoder Beginner Contest 375 A题 代码 #pragma GCC optimize("O2") #pragma GCC optimize("O3") #include <bits/stdc.h> using namespace std; #define int long long const int N 2e5 5, M 1e6 5; const int inf 0x3f3f3f3f3f…...

认识maven
什么是 Maven? Maven 是一个开源的项目管理工具,主要用于 Java 项目的构建、依赖管理和项目生命周期管理。它提供了一种标准的项目结构和管理流程,使得开发人员能够更轻松地管理项目的构建过程,提高代码的可重用性和可维护性。 …...

OSINT技术情报精选·2024年10月第2周
OSINT技术情报精选2024年10月第2周 2024.10.16版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 1、亿欧智库:《2024中国高精定位服务产业白皮书》 报告的主要内容如下: 产业背景:在“北斗”发展态势的…...