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

bittorrent-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' key
      at 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' key
      at 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' key
      at 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' key
      at 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 Error
      at 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' key
      at 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' key
      at 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' key
      at 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' key
      at 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 Error
      at 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 Error
      at 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' key
      at 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' key
      at 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' key
      at 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' key
      at 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 out
      at 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 out
      at 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 out
      at 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 out
      at 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)
})
/**
 * 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 = this
  var isMutable = opts.k || opts.sig
  if (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 = this
  var pending = 0
  var errors = []
  var isMutable = opts.k || opts.sig
  var 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._port
    if (isMutable) {
    
    
      if (opts.cas) localData.cas = opts.cas
      localData.sig = opts.sig
      localData.k = opts.k
      localData.seq = opts.seq
      localData.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 hash

  function put (node) {
    
    
    if (node.data) return // skip data nodes
    pending += 1
    var 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.sig
      data.a.k = opts.k
      if (opts.salt) data.a.salt = opts.salt
      if (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.addr
        errors.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-ok
  var 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函数发布用户的可变密钥到 DHT
  function publish_mutable_key() {
    
    
    var json = JSON.stringify(userProfile); // 将用户仓库信息序列化为 JSON
    if (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);
    // 将可变密钥发布到 DHT
    dht.put(opts, function (errors, hash) {
    
    
      console.error('errors=', errors);
      console.log('hash=', hash.toString('hex')); // 打印发布的哈希值
    });
  }
  

  // 创建一个 TCP 服务器,传入一个回调函数,该函数在每次有新的连接时被调用
  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); // 监听配置中定义的端口
});

猜你喜欢

转载自blog.csdn.net/ResumeProject/article/details/142965887