• bep003-Torrent文件解析.md


    前言

    我对BT相关协议很感兴趣, 萌生了自己实现一个PT Tracker服务端的想法。 顺便学习下Koitln语言, 于是有了 https://github.com/blanexie/vxph 这个项目, 本项目使用的是 JVM17+Kotlin + Gradle + SpringBoot + Sqlite的框架组合方式。

    Bep003 协议是BT协议族的最基础协议。 本篇先讲述下这个协议的内容, 在描述下本项目是怎么实现这个协议的。

    • 协议地址: https://www.bittorrent.org/beps/bep_0003.html
    • 文章中相关代码的开源地址: https://github.com/blanexie/vxph

    Bencode 编码

    Torrent文件和所有的BT协议中的内容都是使用这个编码的。 具体详细可以百度了解下

    协议中的简单描述:

    • 字符串是以长度为前缀的十进制,后跟冒号和字符串。例如,4:spam 对应于“spam”。
    • 整数由“i”表示,后跟以 10 为基数的数字 后跟“e”。例如,i3e 对应于 3,i-3e 对应于 -3。整数没有大小 限度。i-0e 无效。所有带有前导的编码 零,比如 i03e,是无效的,除了 i0e,当然对应于 0。
    • 列表被编码为“l”,后跟其元素(也 bencoded),后跟“e”。例如,l4:spam4:eggse 对应于 [‘spam’, ‘eggs’]。
    • 字典编码为“d”,后跟交替列表 键及其相应的值,后跟“e”。例如,d3:cow3:moo4:spam4:eggse 对应于 {‘cow’: ‘moo’, ‘spam’: ‘eggs’} 和 d4:spaml1:a1:bee 对应于 {‘spam’: [‘a’, ‘b’]}。键必须是字符串,并按排序顺序显示 (按原始字符串排序,而不是字母数字)。
    • 关于字符串的字符集, 十分混乱。 建议还是使用ISO_8859_1进行编码, 中文也是先转成字节数组,在使用这个字符集编码
    • 字节数组的编码,协议中也没有说明, 不过通用的还是先用ISO_8859_1编码成字符串,在使用bencode编码

    metainfo files 文件结构

    文件结构分两种, 单文件结构和多文件结构

    bep003中定义的内容比较少,但是下面的树形结构中内容很多, 多余的内容不用管,都是后面补充协议加上去的

    多文件Torrent的结构

    ├─announce:Tracker的主服务器
    ├─announce-list:Tracker服务器列表
    ├─comment:种子文件的注释
    ├─comment.utf-8:种子文件注释的utf-8编码
    ├─creation date:种子文件建立的时间,是从1970年1月1日00:00:00到现在的秒数。
    ├─encoding:种子文件的默认编码,比如GB2312,Big5,utf-8等
    ├─info:所有关于下载的文件的信息
    │ ├─files:表示文件的名字
    │ │ ├─length:文件的大小(Byte)
    │ │ ├─path:文件的名字,在下载时不可更改
    │ │ └─path.utf-8:文件名的UTF-8编码,同上
    │ ├─name:推荐的文件夹名,此项可于下载时更改
    │ ├─name.utf-8:推荐的文件夹名的utf-8编码,同上
    │ ├─piece length:每个文件块的大小(Byte)
    │ ├─pieces:文件的特征信息(将所有文件按照piece length的字节大小分成块,每块计算一个SHA1值,然后将这些值连接起来所组成)
    │ ├─publisher:文件发布者的名字
    │ ├─publisher-url:文件发布者的网址
    │ ├─publisher-url.utf-8:文件发布者网址的utf-8编码
    │ └─publisher.utf-8:文件发布者的名字的utf-8编码
    └─nodes:这个字段包含一系列ip和相应端口的列表,是用于连接DHT初始node

    单文件Torrent的结构

    ├─announce:Tracker的主服务器
    ├─announce-list:Tracker服务器列表
    ├─comment:种子文件的注释
    ├─comment.utf-8:种子文件注释的utf-8编码
    ├─creation date:种子文件建立的时间,是从1970年1月1日00:00:00到现在的秒数。
    ├─encoding:种子文件的默认编码,比如GB2312,Big5,utf-8等
    ├─info:所有关于下载的文件的信息
    │ ├─length:文件的大小(Byte)
    │ ├─name:推荐的文件夹名,此项可于下载时更改
    │ ├─name.utf-8:推荐的文件夹名的utf-8编码,同上
    │ ├─piece length:每个文件块的大小(Byte)
    │ ├─pieces:文件的特征信息(将所有文件按照piece length的字节大小分成块,每块计算一个SHA1值,然后将这些值连接起来所组成)
    │ ├─publisher:文件发布者的名字
    │ ├─publisher-url:文件发布者的网址
    │ ├─publisher-url.utf-8:文件发布者网址的utf-8编码
    │ └─publisher.utf-8:文件发布者的名字的utf-8编码
    └─nodes:这个字段包含一系列ip和相应端口的列表,是用于连接DHT初始node

    Tracker 下载客户端上报

    Tracker可以理解成BT的注册中心, 它记录各个下载客户端信息的服务,
    各个客户端也可以从tracker中获取其他下载或者做种的用户。

    客户端一般都是使用HTTP GET 请求来上报给tracker信息。 上报的参数如下:

    • info_hash – 这是 .torrent 文件的 info 段的 SHA-1 哈希值,它是个经过 URL 编码的二进制字符串
    • peer_id – 由客户端自行生成的唯一随机 ID,由于在 BT 网络中区分不同的客户端,部分客户端的 peer_id 可能包含不可读字符
    • uploaded – 代表本次会话期间,客户端一共上传了多少字节
    • downloaded – 代表本次会话期间,客户端一共下载了多少字节
    • left – 代表客户端还剩余多少字节等待下载(注意:由于存在校验环节,left 字段仅供参考)
    • event – 代表该 torrent 的事件, 可选字段。 事件列表 :
      • started 种子开始下载
      • completed 种子下载完成,开始做种
      • stopped 种子停止下载 / 做种,不再活动
      • empty 和没有此字段的情况完全相同

    约定俗成的请求路径为 /announce , 下面是一个真实请求示例: 示例中包含有很多其他的字段,不用管都是BT其他协议引入的
    GET /announce?info_hash=j%25%7c%fe%12%0e%c0%9d%ee6%d5%df%03%bb%fda%cd%7b%97%b5&peer_id=-qB4510-1MTFo0SteXN2&port=22387&uploaded=0&downloaded=0&left=0&corrupt=0&key=332CA113&event=started&numwant=200&compact=1&no_peer_id=1&supportcrypto=1&redundant=0&ipv4=198.18.0.1&ipv6=2408%3a8214%3a2e11%3a8cb1%3a%3a9b8&ipv6=2408%3a8214%3a2e11%3a8cb1%3a7c27%3acac2%3a3b51%3a42fb&ipv6=2408%3a8214%3a2e11%3a8cb1%3ae0e7%3a8eb%3a4677%3a21f0

    后端接收Announce上报接口

    请添加图片描述

    InfoHashParam注解

    由于infoHash是一个20长度的字节数组, Qbitorrent客户端上报的infoHash字段中,SpringBoot的默认参数解析无法正确接码,于是我自定义了接口参数注解和解析方式。

    也许有更好的解决方案,但是我不清楚。 希望知道的人指点。

    • InfoHashParam 注解定义请添加图片描述

    • InfoHashRequestParamResolver 参数解析类
      在这个类中会识别判断@InfoHashParam 并从URL中截取出来参数解析
      请添加图片描述

    • 装配到Spring框架中

    请添加图片描述

    • infoHash解码方法。 从网络上抄来的方法。
      请添加图片描述

    总结

    Bep003协议还包含其它内容, 比如Peer相关的内容, 但是由于我想做的只是Tracker服务,所以文章中没有描述。

    项目中使用到了SaToken作为鉴权方式, 但是由于SaToken还不支持Java17版本,于是我修改了SaToken的相关类使得兼容17。 具体内容会在下篇文章中详述。

  • 相关阅读:
    第一次使用VSCode创建vue项目(报错大全)
    Charles抓包web、手机、小程序配置
    AprilTags c++识别
    【记录】数据处理方法总结及实现
    Activiti学习(一)之工作流的介绍和使用
    深度探索Copilot插件
    XuperChain共建守护者系列藏品震撼发行,最新合成玩法揭秘
    配置服务器SSH
    mysql忘记密码无法登录
    MySQL内部函数介绍
  • 原文地址:https://blog.csdn.net/leisurelen/article/details/134447346