bencode是bittorrent协议专用序列化格式,以i/d/l/数字开头标记整数、字典、列表和字节串,不可用json库解析;需递归下降解析器逐字符处理,注意长度为十进制、key支持任意字节、顶层必须含合法info字典。

什么是Bencode,为什么不能直接用JSON库解析
Bencode是BitTorrent协议专用的序列化格式,不是JSON也不是XML,它用字母开头标记类型:i表示整数、d表示字典、l表示列表、b(或省略)表示字节串。常见错误是拿jsoncpp或nlohmann/json硬套——会直接报错或解析出空对象,因为Bencode没有冒号分隔键值,也没有引号包裹字符串。
典型种子文件开头像这样:d8:announce42:http://tracker.example.com/announce13:creation datei1712345678e,所有字段都是连续拼接,无空格,必须按规则逐字符读取并递归解析。
手写解析器的关键状态机逻辑
核心是写一个能识别首字符并分支处理的递归下降解析器。不要试图一次性读完再切分——Bencode嵌套深(比如d...d...e...e),必须边读边记位置、边递归边回退。
-
i开头:跳过i,读到e为止,中间转为long long(注意负数和超大整数,BitTorrent规范允许i9223372036854775807e这种int64_max) -
d开头:循环读键值对,每次先解析一个字节串(作为key),再解析任意类型值;键必须是字节串且不能含非ASCII控制字符 -
l开头:逐个解析元素直到遇到e,元素类型任意(可嵌套) - 数字开头(如
8:hello):读冒号前数字得长度,再截取对应字节数——这是最易错点:长度是十进制ASCII,不是hex,且不校验UTF-8,纯二进制
用现成库时要注意的三个坑
推荐用libbencode(C风格)或benode(C++17)这类轻量库,但别直接#include <benode></benode>就开跑:
- 输入缓冲区必须以
\0结尾,否则parse()可能越界读——种子文件本身不含结尾符,要手动std::string data = read_file("xxx.torrent"); data.push_back('\0'); - 字典key默认被当作
std::string,但Bencode key允许任意字节(包括\0),实际应存为std::vector<uint8_t></uint8_t>;若用benode,需传入自定义key_type模板参数 - 解析失败时,有些库只返回
nullptr或抛std::runtime_error,但不告诉你卡在哪一行哪一列——建议先用xxd xxx.torrent | head -n 20人工确认前几十字节是否符合Bencode结构,再调试
验证解析结果是否合法的最小检查项
即使解析成功,也不代表是有效种子文件。BitTorrent协议要求顶层字典必须包含info字段,且info本身必须是字典(不是字符串或列表)。常见无效情况:
-
info字段缺失 → 不是种子文件,可能是损坏或伪造数据 -
info是字符串 → 实际是base32编码的infohash,不是原始info字典 -
info字典里没pieces(多文件)或length(单文件)→ 无法计算piece hash,下载会失败 -
pieces长度不是20的倍数 → 每个piece hash必须20字节,少一字节就全盘校验失败
这些检查比解析本身更关键——很多“解析成功但无法用”的问题,都卡在info结构校验上。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











