【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…...
 
大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...
 
P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...
 
企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...
 
网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...
 
基于PHP的连锁酒店管理系统
有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发,数据库mysql,前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...
OD 算法题 B卷【正整数到Excel编号之间的转换】
文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的:a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...
学习一下用鸿蒙DevEco Studio HarmonyOS5实现百度地图
在鸿蒙(HarmonyOS5)中集成百度地图,可以通过以下步骤和技术方案实现。结合鸿蒙的分布式能力和百度地图的API,可以构建跨设备的定位、导航和地图展示功能。 1. 鸿蒙环境准备 开发工具:下载安装 De…...
 
保姆级【快数学会Android端“动画“】+ 实现补间动画和逐帧动画!!!
目录 补间动画 1.创建资源文件夹 2.设置文件夹类型 3.创建.xml文件 4.样式设计 5.动画设置 6.动画的实现 内容拓展 7.在原基础上继续添加.xml文件 8.xml代码编写 (1)rotate_anim (2)scale_anim (3)translate_anim 9.MainActivity.java代码汇总 10.效果展示 逐帧…...
 
WebRTC调研
WebRTC是什么,为什么,如何使用 WebRTC有什么优势 WebRTC Architecture Amazon KVS WebRTC 其它厂商WebRTC 海康门禁WebRTC 海康门禁其他界面整理 威视通WebRTC 局域网 Google浏览器 Microsoft Edge 公网 RTSP RTMP NVR ONVIF SIP SRT WebRTC协…...
 
JDK 17 序列化是怎么回事
如何序列化?其实很简单,就是根据每个类型,用工厂类调用。逐个完成。 没什么漂亮的代码,只有有效、稳定的代码。 代码中调用toJson toJson 代码 mapper.writeValueAsString ObjectMapper DefaultSerializerProvider 一堆实…...
