Hi there 👋

  • ✨ 欢迎来到我的个人博客

[BT-0x00] 聊聊Bencode的Byte String

Bencode作为一种编码二进制数据的方式,是BitTorrent协议中重要的组成部分之一。 它既是.torrent文件的编码格式,也是BitTorrent协议中的消息编码格式,还是DHT协议中的消息编码格式。 本篇博客虽然不会讲解Bencode的具体实现,但是为了聊聊Bencode中的一些不足之处(Byte String),我首先还是要先介绍一下Bencode的编码规则。 具体的官方文档可以参考 这里。 Bencode的编码规则其实相当简单,只有四种基本类型: Integer 就是整数,可以为负数,以i开头,以e结尾,中间是整数的十进制表示,比如i123e表示整数123,又比如i-123e表示整数-123。当为0时,不可以有-号,比如i-0e是不合法的。只能是 i0e。(规范没有规定整数的最大长度) Byte String 可以简单的理解为字符串(实际上是字节字符串 Byte String), 以字符串的长度开头,后面跟着一个冒号:,然后是字符串的内容,比如4:spam表示字符串spam。可以为空字符串,例如0:。(规范没有规定字符串的最大长度) List 就是列表,以l开头,以e结尾,中间是列表的元素,可以是任意Bencode类型的值,比如l5:hello5:worldi123e表示列表["hello", "world", 123]。可以为空列表,例如le。 Dictionary 就是字典,以d开头,以e结尾,中间是字典的键值对,其中键只能是Byte String类型,值可以是Bencode4中类型中的任意类型,比如d4:name4:john3:agei18ee表示字典{"name": "john", "age": 18}。 可以为空字典,例如de。 根据上面的规则,我们可以总结出如下的规律: Bencode无法编码浮点型数据,如果实在需要编码,可以用Byte String代替(二进制编码)。 Bencode的列表和字典是可以嵌套的,比如列表中的元素和字典中的值可以是任意Bencode基本类型。 Bencode的字典中的键必须是Byte String类型。 Bencode的优点 说完了Bencode的编码规则,我先说我认为Bencode的优点: Bencode的编码规则简单,满足了常用的数据类型,支持嵌套,可以表示复杂的数据结构,同时也可以编码二进制数据。 Bencode是顺序编码,每一种数据类型的起始和结束都是固定的,例如列表的起始是l,结束是e,字典的起始是d,结束是e,字节字符串的起始是字符串的长度,结束是字符串的内容,这样的编码方式,使得Bencode的解码非常简单,只需要按照顺序,就可以流式解析每一个字节,天然支持递归解析。节省内存的同时,也提高了解码的效率。 数据结构和JSON类似,但是比JSON更加简单。 Bencode的缺点 Bencode的缺点也是显而易见的,这是在我用Node.js以及Deno分别实现过一个Bencode编解码器之后发现的。 我认为Bencode的最大的缺点就是Byte String没有编解码规则。 Byte String 在原文档中的定义是:字节字符串,也就是二进制字符串,它的编码规则是:以字符串的长度开头,后面跟着一个冒号:,然后是字符串的内容,比如4:spam表示字符串spam。可以为空字符串,例如0:。 我们知道在使用utf-8编码的情况下,一个字节可以表示一个ASCII字符,而一个中文字符需要3个字节,所以在Bencode中,一个中文字符需要用3个字节来表示,比如3:中。 Bencode默认Byte String是二进制字节流,但是并没有规定具体的编码方式,社区默认Byte String的字节如果都是合法UTF-8字节,就解析成UTF-8字符串,如果包含非法UTF-8字节,就不解析,返回对应的字节数组。 所以这就导致了问题的出现,看下面两个例子: 1.当ByteString作为字典的值时 当ByteString作为字典的值时,如果此时的ByteString包含非UTF-8编码的字符,那么在解码的时候,就需要小心。 因为在Node.js和Deno中,使用TextDecoder解码一个字节数组时,如果字节数组中包含非UTF-8编码的字符,那解码后的字符串,就会丢失数据。 此时如果你再将解码后的字符串编码成字节数组,那么就会得到一个和原来的字节数组不一样的字节数组,这就导致了解码后的数据和编码前的数据不一致。 const bytes = Uint8Array.from([0xff, 0x32, 0x33, 0xe4]) const str = new TextDecoder().decode(bytes) const newBytes = new TextEncoder()....

九月 3, 2023 · Sloaix

Hello World !

前言 经过一番思考,我还是决定将这篇博客命名为《Hello World!》。这是我八年来重新开设博客后的第一篇文章,我还记得当初的第一篇博客也是《Hello World!》。 谈到为什么在时隔八年后重新开设博客,这与 Deno 有关。你可以看到文章开头的图片是 Deno 的像素风logo,一只在雨中的小恐龙。Deno 是一个新的JavaScript运行时环境,其作者与 Node.js 的创造者相同,而其名称恰恰是Node的反义词。 我之所以知道 Deno ,是因为在2018年的某一天,我看到 Deno 在v2ex上引起了很高的关注,于是我开始了解它,虽然JavaScript并不是我擅长的语言。 当时 Deno 刚刚发布不久,但它支持通过URL导入包、内置的TypeScript支持以及内置的安全机制等功能让我非常感兴趣。然而,由于我在百度的工作非常繁忙,所以我没有深入研究,只是暗自期待 Deno 的发展。 不知不觉中, Deno 已经发展到了v1.36.3,而我也已经离开百度,离开北京,回到了重庆。生活的节奏慢了下来,我也有更多的时间关注我感兴趣的事情。从绘画到音乐、从游戏到滑板、最后我能坚持下来的还是编程。 最近,我开始用 Deno 从零开始编写一个BitTorrent下载器。你可能会问为什么不使用现成的下载器?这正是我下面会说到的。 起因 我不知道从什么时候开始,国内的番剧爱好者慢慢成为了正版的受害者,我也很久没有使用国内的平台追番了。 90后从小时候的电视、影碟,到后来的迅雷、网盘,最后到现在的B站、爱奇艺、腾讯视频、优酷等等。观看动漫的形式越来越方便了,但是可以看到的动漫却越来越少了,质量也越来越差了。 随着国内引进的动漫越来越少,删减的部分也越来越多,我重新回到了BT的怀抱。那段时间,我刚好购买了人生中第一台NAS——群晖1019+,于是我开始折腾家庭媒体服务器。 我先后尝试了Emby、Jellyfin、Plex,最终成为了Plex的忠实用户。Plex在元数据刮削和对不同系统客户端的支持上是我使用过的媒体服务器软件中最好的。然而,即使是刮削功能出色的Plex,也无法解决不同字幕组和番剧命名混乱的问题。 Plex能够识别像"S01E01"这样的命名方式,但对于类似"[SubsPlease] Tokyo Revengers - 01 [1080p][Multiple Subtitle].mkv"这样的命名,Plex就无能为力了。 因此,我开始尝试一些解决方案。起初,我用Python手写了一个简单的脚本,用于提取文件名中的集数信息并重命名文件。然而,这个脚本改名后的文件无法被qBittorrent继续做种,而且qBittorrent提供的重命名功能也无法满足我的自动化需求。且脚本独立于下载器,无法自动触发,依赖于crontab定时任务,需要在NAS上维护,也不是很方便。我开始寻找现有的解决方案,但大多数都无法满足我的需求。 最终,我决定自己编写一个BitTorrent下载器 — CellBit ,可以在自动处理文件重命名的同时做种,同时用插件的形式实现更多的功能。经过一番调查和比较,我决定使用 Deno 。因为用 Deno 的话,通过url导入包,就可以很方便的实现插件的功能。 可以想象,只需要在 CellBit 中用Typescript规定好插件接口规范,让插件开发者实现自己的插件,然后通过url导入插件包,就可以实现插件的功能了。 进展 我大概在1个月前开始了 CellBit 的开发,到目前为止,我连一个最简单的下载器都还没写出来。 Deno 提供了诸多方便的同时,也让我遇到了很多问题。Deno 的诞生距今才几年时间,社区的生态还不是很完善,很多功能都需要自己去实现。 而BT下载器的实现涉及到很多基础设施,就比如 Bencode编码,DHT协议,Tracker协议,Peer Wire协议,这些都需要自己去实现。虽然 Deno 支持npm包导入,但是这些npm包大多都是基于 Node.js 的,很大概率是无法直接在 Deno 中使用的。所以,我不得不自己去实现这些基础设施。这一个月,我也在不断的学习,不断的尝试,不断的摸索,不断的折腾。从最初的 Bencode 都不知道是什么,到现在能够实现一个简单的 DHT协议,我也算是有了一些进步。...

八月 30, 2023 · Sloaix