Torrent文件的解析与转换
阅读目录
- Torrent简介
- Torrent结构
- Torrent文件编码
- Torrent文件解析
- Torrent文件与Magnet
- 具体实现
- Reference
回到顶部
Torrent简介
BitTorrent协议的种子文件(英语:Torrent file)可以保存一组文件的元数据。这种格式的文件被BitTorrent协议所定义。扩展名一般为“.torrent”。
.torrent种子文件本质上是文本文件,包含Tracker信息和文件信息两部分。Tracker信息主要是BT下载中需要用到的Tracker服务器的地址和针对Tracker服务器的设置,文件信息是根据对目标文件的计算生成的,计算结果根据BitTorrent协议内的Bencode规则进行编码。它的主要原理是需要把提供下载的文件虚拟分成大小相等的块,块大小必须为2k的整数次方(由于是虚拟分块,硬盘上并不产生各个块文件),并把每个块的索引信息和Hash验证码写入种子文件中;所以,种子文件就是被下载文件的“索引”。
回到顶部
Torrent结构
Torrent文件内容都以Bencoding编码类型进行存储,整体上是一个字典结构,见下:
Torrent总体结构
键名称数据类型可选项键值含义
announcestringrequiredTracker的Urlinfodictionaryrequired该条映射到一个字典,该字典的键将取决于共享的一个或多个文件announce-listarray[]optional备用Tracker的Url,以列表形式存在commentstringoptional备注created bystringoptional创建人或创建程序的信息
Torrent单文件Info结构
Torrent多文件Info结构
files字典结构:
Torrent实际结构预览
以JSON序列化整个字典后,单文件和多文件的结构大致如下,注意:JSON内容省略了pieces摘要大部分内容,仅展示了开头部分,另外由于本人序列化工具设置所致,所有的整型都会序列化成字符串类型。
- 单文件结构
- 多文件结构
{"creation date": "1604347014","comment": "Torrent downloaded from https://YTS.MX","announce-list": [["udp://tracker.coppersurfer.tk:6969/announce"],["udp://9.rarbg.com:2710/announce"],["udp://p4p.arenabg.com:1337"],["udp://tracker.internetwarriors.net:1337"],["udp://tracker.opentrackr.org:1337/announce"]],"created by": "YTS.AG","announce": "udp://tracker.coppersurfer.tk:6969/announce","info": {"pieces": "ᆲimᅬヒ\u000b*゚ᆲト... ...","name": "Love And Monsters (2020) [2160p] [4K] [WEB] [5.1] [YTS.MX]","files": [{"path": ["Love.And.Monsters.2020.2160p.4K.WEB.x265.10bit.mkv"],"length": "5215702961"},{"path": ["www.YTS.MX.jpg"],"length": "53226"}],"piece length": "524288"}
}
回到顶部
Torrent文件编码
根据上文所说,Torrent文件均以Bencoding编码进行存储,故我们需要大致了解一下Bencoding编码。
Bencoding以四种基本类型数据构成:
- string : 字符串
- intergers : 整数类型
- lists:列表类型
- dictionary:字典类型
字符串类型
字符串类型由以下结构表示:字符串长度:字符串原文,例如:
42:udp://tracker.pirateparty.gr:6969/announce。
整形类型
整形类型由以下结构表示:i<整形数据>e,例如i1234e,则表明的整形数据为1234。
列表类型
列表类型由以下结构表示:l<列表数据>e,即列表以字母l开头,以字母e结束,中间的均为列表中的数据,中间的值可以为任意的四种类型之一。
字典类型
字典类型由以下结构表示:d<字典数据>e,即字典由字母d开头,以字母e结束,中间的均为字典中的数据,中间的值可以为任意的四种类型之一。
实际组合解析
根据上述描述来看看实际的内容解析,我们以下方的数据为例:
d8:announce49:udp://tracker.leechers-paradise.org:6969/announce13:announce-listll49:udp://tracker.leechers-paradise.org:6969/announceel48:udp://tracker.internetwarriors.net:1337/announceeee
大家可以先尝试根据上面的内容对这一串内容进行解析,我将这一串数据拆分开来方便大家理解和查看,可以明显看出其由一个拥有两个键值的字典,其中一个键为announce,另一个键为announce-list,两者的值一个为
udp://tracker.leechers-paradise.org:6969/announce,一个为列表,列表内还嵌套了一层列表。
d8:announce 49:udp://tracker.leechers-paradise.org:6969/announce13:announce-listll49:udp://tracker.leechers-paradise.org:6969/announceel48:udp://tracker.internetwarriors.net:1337/announceee
e
回到顶部
Torrent文件解析
根据上文对Torrent文件编码的了解,那么我们使用代码对Torrent文件就很简单了。我们只需要读取种子字节流,判断具体是哪种类型并进行相应转换即可。
即:读取文件字节,判断字节属于哪一种类型:0-9 : 字符串类型、i:整形数据、l:列表数据、d:字典数据
再根据每个数据具体类型获取该数据的内容,再读取下一个文件字节获取下一个数据类型即可,根据这个分析,伪代码如下:
获取字符串值
// 当读取到字节对应的内容为0-9时进入该方法
String readString(byte[] info,int offset) {// 读取‘:’以前的数据,即字符串长度int length = readLength(info,offset);// 根据字符串长度,获取实际字符串内容string data = readData(info,length,offset);// 返回读取到的字符串内容,整个读取过程中读过的偏移量要累加到offsetreturn data;
}
获取整数类型
这里有一个注意项,考虑到数据边界问题,例如java等语言,推荐使用Long类型,以防数据越界。
// 当读取到的字节对应的内容为i时,进入该方法
Long readInt(byte[] info,int offset) {// 读取第一个'e'之前的数据,包括'e'string data = readInt(info,offset)return Long.valueOf(data);
}
获取列表类型
因为列表类型中可以夹杂所有四种类型中任意要给即需要用到上面两个方法。
// 当读取到的字节对应的内容为l时,进入该方法
List readList(byte[] info,int offset){List list = new List();// 读取到第一个'e'为止while(info[offset] != 'e'){swtich(info[offset]){// 如果是列表,读取列表并向列表添加case 'l':list.add(readList(info,offset));break;// 如果是字典,读取字典并向列表添加case 'd':list.add(readDictionary(info,offset));break;// 如果是整形数据,读取数据并向列表添加case 'i':list.add(readInt(info,offset));break;// 如果是字符串,读取字符串数据并向列表添加case '0-9':list.add(readString(info,offset));}}// offset向前移一位,把列表的结束符'e'移动为已读offset++;return list;
}
读取字典类型
读取字典类型与列表十分相似,唯一不同的就是需要区分键值,字典的键值可能为字符串,故依此来判断。
// 当读取到的字节对应的内容为d时,进入该方法
Dictionary readDictionary(byte[] info,int offset){Dictionary dic = new Dictionary();// key为null时,字符串为键,否则为值 String key = null;// 读取到第一个'e'为止while(info[offset] != 'e'){swtich(info[offset]){// 如果是列表,读取列表并向字典添加,添加列表时肯定存在键,直接添加并将键置空case 'l':dic.put(key,readList(info,offset));key = null;break;// 如果是字典,读取字典并向字典添加,添加字典时肯定存在键,直接添加并将键置空case 'd':dic.put(key,readDictionary(info,offset));key = null;break;// 如果是整形数据,读取数据并向字典添加,添加整形数据时肯定存在键,直接添加并将键置空case 'i':dic.put(key,readInt(info,offset));key = null;break;// 如果是字符串case '0-9':string data = readString(info,offset);// key为null时,字符串为键,否则为值 if(key == null){key = data;}else{dic.put(key,data);key = null;}}}// offset向前移一位,把列表的结束符'e'移动为已读offset++;return dic;
}
回到顶部
Torrent文件与Magnet
磁力链接与Torrent文件是可以相互转换的,此文只讨论根据Torrent文件如何转换为Magnet磁力链接。
Magnet概述
磁力链接由一组参数组成,参数间的顺序没有讲究,其格式与在HTTP链接末尾的查询字符串相同。最常见的参数是"xt",是"exact topic"的缩写,通常是一个特定文件的内容散列函数值形成的URN,例如:
magnet:?xt=urn:bith:YNCKHTQCWBTRNJIV4WNAE52SJUQCZO5C
注意,虽然这个链接指向一个特定文件,但是客户端应用程序仍然必须进行搜索来确定哪里,如果有,能够获取那个文件(即通过DHT进行搜索,这样就实现了Magnet到Torrent的转换,本文不讨论)。
部分字段名见下方表格:
字段名含义magnet协议名xtexact topic的缩写,包含文件哈希值的统一资源名称。BTIH(BitTorrent Info Hash)表示哈希方法名,这里还可以使用ED2K,AICH,SHA1和MD5等。这个值是文件的标识符,是不可缺少的。dndisplay name的缩写,表示向用户显示的文件名。这一项是选填的。trtracker的缩写,表示tracker服务器的地址。这一项也是选填的。bithBitTorrent info hash,种子散列函数
Torrent转换为Magnet
- dn : 向用户显示的文件名
即为Torrent文件中,Info字典下的name键所对应的值
- tr : tracker服务器地址
即为Torrent文件中,announce以及announce-list两个键所对应的值
- bitch : 种子散列值
即为Torrent文件中,info对应的字典的SHA1哈希值(Hex)
根据下图,为4:infod,以d的地址作为哈希原文的起始索引,则为Adress:00 01A3
到整个info结束,以e的地址作为哈希原文的终止索引地址,则为Adress:03 0BE7
根据上述可知:
magnet = 'magnet:?xt=urn:btih:'+Hex(Sha1(info))+'&dn='+encode(name)+'&tr='+encode(announce)
结合上一部分的实现,我们可以在读取info时记录startindex和endindex,即:
Dictionary readDictionary(byte[] info,int offset){//...case 'd':bool record = key == 'info';if(record){startindex = offset;}readDictoinary(info,offset);if(record){endindex = offset}
}string getBith(byte[] info,int start,int end){// 获取info中从start到end的字节数组,并对其进行摘要计算byte[] infoByte = new byte[infoEnd - infoStart + 1];System.arraycopy(torrentBytes, infoStart, infoByte, 0, infoEnd - infoStart + 1);return Hex.toHex(Sha1.toSha1(infoByte));
}
回到顶部
具体实现
本人通过Java实现了以上部分逻辑(Torrent文件解析以及Magnet链接生成),若有需要参考的读者可以到以下网址获取相关内容:
工具类目录:
https://github.com/Rekent/common-utils/tree/master/src/main/java/com/rekent/tools/utils/torrent
解析类源码:
https://github.com/Rekent/common-utils/blob/master/src/main/java/com/rekent/tools/utils/torrent/TorrentFileResovler.java
依赖jar包:
https://github.com/Rekent/common-utils/releases/tag/v0.0.3
调用方式:
public void testResolve() throws Exception {String path = "C:\\Users\\Refkent\\Downloads\\Test.torrent";TorrentFile torrentFile = TorrentFileUtils.resolve(path);System.out.println(torrentFile.print());System.out.println(torrentFile.getHash());System.out.println(torrentFile.getMagnetUri());
}
Torrent文件的解析与转换相关推荐
- python解析mht文件_php解析mht文件转换成html的方法
本篇文章主要介绍php解析mht文件转换成html的方法,感兴趣的朋友参考下,希望对大家有所帮助. php解析mht文件,使用编辑器打开可以看到base64编码所以,mht是可以转换成html的. / ...
- linux 文件指针,Linux中文件描述符fd与文件指针FILE*互相转换实例解析
本文研究的主要是Linux中文件描述符fd与文件指针FILE*互相转换的相关内容,具体介绍如下. 1.文件描述符fd的定义:文件描述符在形式上是一个非负整数.实际上,它是一个索引值,指向内核为每一个进 ...
- php解析torrent文件,PHP基于闭包思想实现的BT(torrent)文件解析工具实例详解
本文实例讲述了PHP基于闭包思想实现的torrent文件解析工具.分享给大家供大家参考,具体如下: PHP对静态词法域的支持有点奇怪,内部匿名函数必须在参数列表后面加上use关键字,显式的说明想要使用 ...
- python解析mht文件_php解析mht文件转换成html的实例详解
下面小编就为大家带来一篇php解析mht文件转换成html的实例.小编觉得挺不错的,现在就分享给大家,也给大家做个参考.一起跟随小编过来看看吧 php解析mht文件,使用编辑器打开可以看到base64 ...
- C#怎样解析.CSV文件同时把类转换成JSON
1.首先需要引用 Newtonsoft.Json 目的是为了把对象类直接转换为JSON,返回到前台 using Newtonsoft.Json; 2.定义一个类,存储返回的值 public class ...
- php解析bt,PHP基于闭包思想实现的BT(torrent)文件解析工具实例详解
PHP基于闭包思想实现的BT(torrent)文件解析工具实例详解 发布于 2017-09-08 20:05:36 | 124 次阅读 | 评论: 0 | 来源: 网友投递 PHP开源脚本语言PHP( ...
- torrent文件解析器
第二步工作是解析torrent文件,有了bencoding编码解析器 解析torrent文件当然是易如反掌的任务了. 实现的封装类CTorrentParser,完成的主要任务有: 1.判断torren ...
- Python3 解析 torrent 文件
(可直接转到文末下载) 起因: 找资源的时候,在某些网站下载到torrent文件.虽说挺常见的,但是这玩意只能用第三方软件打开,总觉得不爽.另外也是单纯出于好奇心,想看看这玩意长什么样子,一探究竟. ...
- [C#]Bencode编码算法/torrent文件解析
最近工作上的事少了些,便想起了打入冷宫N久的博客,于是找了些小玩意做做,放到博客和github上头当是给有兴趣的朋友一起交流. 首先是想简单说下torrent文件的格式,torrent文件其实说白了就 ...
最新文章
- C++中fstream的使用
- 一文读懂机器学习项目的完整生命周期
- cordova sqlite
- tomcat9扩展php 插件,Eclipse插件开发tomcat扩展
- uniapp商城_【程序源代码】商城小程序
- LOJ #516. 「LibreOJ β Round #2」DP 一般看规律
- JAVA 类加载 随记
- 黑马程序员---java基础------------------多线程
- Visual studio 2005 恢復默認設置的方法
- SQL联合主键 查重
- WebStorm设置自动刷新
- 详解JavaScript数组过滤相同元素的5种方法
- 清华姚班毕业生开发新特效编程语言,99行代码实现《冰雪奇缘》,网友:大神碉堡!创世的快乐
- 操作系统中的FCFS(First Come First Served)先来先服务算法,java实现
- 【Redis】模拟手机验证码案例:Jedis使用、get、hget、hincrBy、setex、生成6位随机数工具
- DES加密算法(框图流程详细分析)(C++实现)
- 计算机网络(第8版)期末复习
- 数据库原理(三):Sql Server操作语句
- 人人看得懂的ChatGPT技术原理解析
- 致烟民早日摆脱烟瘾的困扰