You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优化BitTorrent DHT索引infohashes?代码事件异常排查

BitTorrent DHT索引优化与代码问题修复

问题说明

  1. 运行Node.js代码12小时仅捕获5个infohash,需优化提升获取量;
  2. 代码中client.add(magnetURI, (torrent))回调和torrent.on('tracker')事件均未触发。

相关代码

import DHT from 'bittorrent-dht';
import magnet from 'magnet-uri';
import WebTorrent from 'webtorrent';
import redis from 'redis';

const dht = new DHT();
const client = new WebTorrent();

// 创建Redis客户端
const redisClient = redis.createClient({
    host: 'localhost', // 根据你的Redis配置修改
    port: 6379
});

redisClient.on('error', function(error) {
    console.error(error);
});

// 种子节点 - 替换为实际可用节点
const seedNodes = ['router.bittorrent.com:6881', 'dht.transmissionbt.com:6881'];
const knownTorrents = []; // 建议存到数据库
const torrentQueue = []; // 管理torrent获取的队列
const torrentFetchInterval = 10000; // 每次获取torrent的间隔(毫秒)

client.on('torrent', (torrent) => {
    console.log(`Torrent已添加: ${torrent.infoHash}`);
    console.log(`文件列表: ${torrent.files.map((file) => file.name).join(', ')}`);
});

client.on('error', (err) => {
    console.error('WebTorrent客户端错误:', err);
});

// Redis共享状态示例
function updateSharedTorrentState(torrent) {
    // 将torrent数据存入Redis
    redisClient.set(torrent.infoHash, JSON.stringify(torrent), redis.print);

    // 从Redis读取torrent数据
    redisClient.get(torrent.infoHash, (err, reply) => {
        if (err) {
            console.error(err);
            return;
        }
        const sharedTorrent = JSON.parse(reply);
        console.log(sharedTorrent); // 处理共享的torrent数据
    });
}

function processTorrentQueue() {
    if (torrentQueue.length > 0) {
        const infoHashBuffer = torrentQueue.shift(); // 从队列取出下一个torrent
        indexTorrent(infoHashBuffer);
    }
    setTimeout(processTorrentQueue, torrentFetchInterval);
}

function indexTorrent(infoHashBuffer) {
    const infoHash = infoHashBuffer.toString('hex');
    console.log('发现Torrent:', infoHash);

    // 用infohash生成magnet链接
    const magnetURI = `magnet:?xt=urn:btih:${infoHash}`;

    // 检查torrent是否已被追踪
    if (client.get(magnetURI)) {
        console.log(`infohash为${infoHash}的Torrent已被追踪`);
        return; // 已添加则退出函数
    }

    // 用WebTorrent客户端获取元数据
    client.add(magnetURI, (torrent) => {
        console.log('Torrent元数据已获取:', torrent.name);

        // 在knownTorrents数组中添加或更新torrent
        const index = knownTorrents.findIndex((t) => t.infoHash === infoHash);

        if (index > -1) {
            knownTorrents[index] = {
                ...knownTorrents[index],
                name: torrent.name // 仅更新特定属性
            };
        } else {
            knownTorrents.push({
                infoHash,
                name: torrent.name,
                seeders: 0, // 初始化种子数和下载者数
                leechers: 0
            });
        }

        // 'tracker'事件获取tracker数据
        torrent.on('tracker', (data) => {
            console.log(`${torrent.name}的Tracker报告: ${data.announce}`);
            console.log(`种子数: ${data.complete}, 下载者数: ${data.incomplete}`);

            // 更新knownTorrents中的种子数和下载者数
            const torrentIndex = knownTorrents.findIndex((t) => t.infoHash === infoHash);
            if (torrentIndex > -1) {
                knownTorrents[torrentIndex] = {
                    ...knownTorrents[torrentIndex],
                    seeders: data.complete,
                    leechers: data.incomplete
                };
            }
        });

        // 仅需tracker信息,延迟后移除torrent
        setTimeout(() => client.remove(magnetURI), 60000); // 可调整延迟时间
    });
}

// 添加并测试DHT网络节点
function joinAndTestDHTNetwork(seedNodes) {
    console.log('正在加入DHT网络...');

    seedNodes.forEach((seed) => {
        const [host, port] = seed.split(':');
        dht.addNode({ host, port: parseInt(port) });
    });

    // 加入后测试节点查询
    setTimeout(() => {
        console.log('测试DHT网络节点查询...');
        dht.nodes.toArray().forEach((node) => {
            dht.lookup(node.id, (err, n) => {
                if (err) {
                    console.error(`查询节点${node.id}失败:`, err);
                } else {
                    console.log(`成功查询节点${node.id.toString('hex')},发现${n}个节点`);
                }
            });
        });
    }, 5000); // 延迟等待初始网络加入完成
}

// 处理announce事件
dht.on('announce', (peer, infoHashBuffer) => {
    // 将torrent加入队列而非立即处理
    torrentQueue.push(infoHashBuffer);
});

// 处理新节点发现事件
dht.on('node', (node) => {
    console.log(`发现新节点: ${node.host}:${node.port}`);
    // 查询随机infohash以发现更多节点和peer
    dht.lookup(generateRandomInfoHash());
});

// 定期查询随机infohash
setInterval(() => {
    dht.lookup(generateRandomInfoHash());
}, 10000); // 每10秒一次,可调整

// 生成随机infohash用于查询
function generateRandomInfoHash() {
    let infoHash = '';
    for (let i = 0; i < 40; i++) {
        infoHash += Math.floor(Math.random() * 16).toString(16);
    }
    return infoHash;
}

setInterval(() => {
    knownTorrents.forEach((torrent) => {
        indexTorrent(Buffer.from(torrent.infoHash, 'hex'));
    });
}, 3600000);

// 示例执行
processTorrentQueue();
joinAndTestDHTNetwork(seedNodes);

dht.listen(20000, () => {
    console.log('DHT监听端口20000');
});

解决方案

一、提升infohash获取数量

  • 扩展种子节点池:
    增加更多公共DHT种子节点,比如router.utorrent.com:6881、dht.aelitis.com:6881、tracker.opentrackr.org:1337,扩大网络覆盖范围。
  • 优化DHT节点交互逻辑:
    移除node事件中每次发现新节点就查询随机infohash的操作,频繁随机查询会消耗大量带宽且效率极低;改为对已捕获的infohash进行lookup,或者批量处理节点发现请求。
  • 调整查询频率:
    将随机infohash查询间隔从10秒延长至30-60秒,避免过度请求被节点限制。
  • 实现infohash去重:
    用Redis集合存储已处理的infohash,替代内存数组knownTorrents,避免队列堆积重复项,同时支持持久化和分布式部署。
  • 优化网络可达性:
    确保DHT监听的端口(20000)可被外部访问,若在NAT环境下需配置端口映射,否则无法接收外部节点的announce请求。
  • 调整队列处理效率:
    减小torrentFetchInterval至2-5秒,同时限制并发处理的torrent数量(比如最多同时处理5个),避免资源耗尽。

二、修复回调与事件不触发问题

  • 避免DHT实例冲突:
    WebTorrent默认会启动内置DHT实例,与单独初始化的bittorrent-dht实例冲突,初始化时禁用内置DHT:
    const client = new WebTorrent({ dht: false });
    
  • 排查元数据获取失败问题:
    client.add回调仅在成功获取元数据时触发,添加错误监听排查原因:
    const torrent = client.add(magnetURI, (torrent) => {
        // 原回调逻辑
    });
    torrent.on('error', (err) => {
        console.error(`获取${infoHash}元数据失败:`, err);
    });
    
  • 替代tracker事件的方案:
    很多DHT中的torrent无有效tracker,tracker事件自然不会触发,可通过wire事件统计peer数量:
    torrent.on('wire', (wire, addr) => {
        console.log(`新Peer连接: ${addr}`);
        // 自行维护seeders/leechers计数
    });
    
    若需tracker数据,手动为magnet链接添加公共tracker:
    const publicTrackers = [
        'udp://tracker.openbittorrent.com:80',
        'udp://tracker.opentrackr.org:1337'
    ];
    const magnetURI = `magnet:?xt=urn:btih:${infoHash}&tr=${publicTrackers.join('&tr=')}`;
    

内容的提问来源于stack exchange,提问作者chovy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 13:07:04