调试自定义Python libtorrent客户端:DHT路由添加报错及节点为0问题
问题解决:libtorrent DHT节点为0、tracker_reply_alert未触发及add_dht_router参数错误
问题概述
- DHT已启用但节点数始终为0
tracker_reply_alert未触发,且tracker返回错误码22- 调用
ses.add_dht_router()时出现Boost.Python参数不匹配错误
错误详情
调用add_dht_router()时抛出的错误:
Boost.Python.ArgumentError: Python argument types in session.add_dht_router(session) did not match C++ signature: add_dht_router(libtorrent::session {lvalue}, std::__cxx11::basic_string<char, std::char_traits
, std::allocator > router, int port)
当前脚本输出:
DHT enabled: True DHT nodes: 0
问题分析与解决方案
1. add_dht_router参数错误
从错误信息可明确,add_dht_router()需要传入两个必填参数:引导节点地址和端口,你当前调用时未传参,导致参数不匹配。
正确调用方式示例:
# 添加官方DHT引导节点 ses.add_dht_router("router.bittorrent.com", 6881) ses.add_dht_router("dht.transmissionbt.com", 6881) ses.add_dht_router("router.utorrent.com", 6881)
2. DHT节点数为0的问题
- 确保调用
ses.start_dht()启动DHT服务(你的代码中未显式启动,默认可能未激活) - 添加引导节点后需等待一段时间,让节点完成发现与连接,不要立即检查节点数
- 检查
listen_interfaces配置:你使用的127.0.0.5:6881是本地回环地址,无法与外部DHT节点通信,建议改为0.0.0.0:6881或公网IP
修改后的session初始化示例:
settings = { 'user_agent': 'python-client/' + lt.__version__, 'listen_interfaces': '0.0.0.0:6881', # 改为允许外部连接的地址 'download_rate_limit': 0, 'upload_rate_limit': 10*1024*1024, 'alert_mask': lt.alert.category_t.all_categories, 'outgoing_interfaces': '', } ses = lt.session(settings) # 启动DHT ses.start_dht() # 添加引导节点 ses.add_dht_router("router.bittorrent.com", 6881) ses.add_dht_router("dht.transmissionbt.com", 6881)
3. tracker_reply_alert未触发及错误码22
错误码22对应libtorrent的invalid_argument,可能原因:
- tracker地址无效或无法访问
- 本地网络限制(防火墙、代理)导致无法连接tracker
- 检查torrent文件中的tracker地址是否正确,或尝试添加公共tracker
另外,tracker_reply_alert仅在tracker成功返回时触发,连接失败会触发tracker_error_alert,建议添加该alert的处理逻辑:
if isinstance(a, lt.tracker_error_alert): print(f"Tracker error: {a.message()}, error code: {a.error.value()}")
完整修改后的代码
import libtorrent as lt import time import os.path from loggers import logger def add_torrent(ses, filename, save_path): atp = lt.add_torrent_params() ti = lt.torrent_info(filename) # resume_file = os.path.join(save_path, # ti.name() + '.fastresume') # try: # # Try to read resume file # atp = lt.read_resume_data(open(resume_file, 'rb').read()) # except Exception as e: # logger.exception(f"Failed to open resume file '{resume_file}': {str(e)}") atp.ti = ti atp.save_path = save_path atp.storage_mode = lt.storage_mode_t.storage_mode_sparse atp.flags |= lt.torrent_flags.duplicate_is_error \ | lt.torrent_flags.auto_managed ses.async_add_torrent(atp) def add_suffix(val): # 单位解析逻辑参考client.py,此处不影响核心功能 pass settings = { 'user_agent': 'python-client/' + lt.__version__, 'listen_interfaces': '0.0.0.0:6881', 'download_rate_limit': 0, 'upload_rate_limit': 10*1024*1024, 'alert_mask': lt.alert.category_t.all_categories, 'outgoing_interfaces': '', } ses = lt.session(settings) # 启动DHT并添加引导节点 ses.start_dht() ses.add_dht_router("router.bittorrent.com", 6881) ses.add_dht_router("dht.transmissionbt.com", 6881) ses.add_dht_router("router.utorrent.com", 6881) torrents = {} alerts_log = [] torrent_path = "/path/to/torrent/file/ubuntu-23.10.1-desktop-amd64.iso.torrent" save_path = "/my/save/path/torrents/" add_torrent(ses, torrent_path, save_path) alive = True while alive: print(f"DHT enabled: {ses.is_dht_running()}") print(f"DHT nodes: {ses.status().dht_nodes}") for h, t in torrents.items(): if t.state != lt.torrent_status.seeding: print(f"Error state: {t.error}") print(f"Torrent status: {t.paused}") print(f"Trackers: {h.trackers()}") alerts = ses.pop_alerts() for a in alerts: alerts_log.append(a.message()) if isinstance(a, lt.add_torrent_alert): h = a.handle h.set_max_connections(60) h.set_max_uploads(-1) torrents[h] = h.status() if isinstance(a, lt.state_update_alert): for s in a.status: torrents[s.handle] = s if isinstance(a, lt.tracker_reply_alert): print(f"Tracker返回节点数: {a.num_peers}") if isinstance(a, lt.tracker_error_alert): print(f"Tracker错误: {a.message()}, 错误码: {a.error.value()}") if len(alerts_log) > 20: alerts_log = alerts_log[-20:] time.sleep(0.5) ses.post_torrent_updates() ses.pause() for h, t in torrents.items(): if not h.is_valid() or not t.has_metadata: continue h.save_resume_data() while len(torrents) > 0: alerts = ses.pop_alerts() for a in alerts: if isinstance(a, lt.save_resume_data_alert): print(a) data = lt.write_resume_data_buf(a.params) h = a.handle if h in torrents: with open( os.path.join(save_path, torrents[h].name + '.fastresume'), 'wb') as f: f.write(data) del torrents[h] if isinstance(a, lt.save_resume_data_failed_alert): h = a.handle if h in torrents: print('保存续传数据失败: ', torrents[h].name) del torrents[h] time.sleep(0.5)
内容的提问来源于stack exchange,提问作者chocoloco
相关产品推荐
相关产品推荐

