当前位置: 首页 > 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…...

Chapter03-Authentication vulnerabilities

文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

【Linux】shell脚本忽略错误继续执行

在 shell 脚本中&#xff0c;可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行&#xff0c;可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令&#xff0c;并忽略错误 rm somefile…...

Spark 之 入门讲解详细版(1)

1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室&#xff08;Algorithms, Machines, and People Lab&#xff09;开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目&#xff0c;8个月后成为Apache顶级项目&#xff0c;速度之快足见过人之处&…...

Java - Mysql数据类型对应

Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!

简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求&#xff0c;并检查收到的响应。它以以下模式之一…...

IP如何挑?2025年海外专线IP如何购买?

你花了时间和预算买了IP&#xff0c;结果IP质量不佳&#xff0c;项目效率低下不说&#xff0c;还可能带来莫名的网络问题&#xff0c;是不是太闹心了&#xff1f;尤其是在面对海外专线IP时&#xff0c;到底怎么才能买到适合自己的呢&#xff1f;所以&#xff0c;挑IP绝对是个技…...

uniapp 开发ios, xcode 提交app store connect 和 testflight内测

uniapp 中配置 配置manifest 文档&#xff1a;manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号&#xff1a;4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...

【深度学习新浪潮】什么是credit assignment problem?

Credit Assignment Problem(信用分配问题) 是机器学习,尤其是强化学习(RL)中的核心挑战之一,指的是如何将最终的奖励或惩罚准确地分配给导致该结果的各个中间动作或决策。在序列决策任务中,智能体执行一系列动作后获得一个最终奖励,但每个动作对最终结果的贡献程度往往…...

热门Chrome扩展程序存在明文传输风险,用户隐私安全受威胁

赛门铁克威胁猎手团队最新报告披露&#xff0c;数款拥有数百万活跃用户的Chrome扩展程序正在通过未加密的HTTP连接静默泄露用户敏感数据&#xff0c;严重威胁用户隐私安全。 知名扩展程序存在明文传输风险 尽管宣称提供安全浏览、数据分析或便捷界面等功能&#xff0c;但SEMR…...