当前位置: 首页 > 工具软件 > go-torrent > 使用案例 >

Golang ------ torrent文件解析

端木权
2023-12-01
  1. Golang ------ torrent文件解析
  2. Golang ------ torrent文件下载 (1)
  3. Golang ------ torrent文件下载 (2)

bencode编码

.torrent文件使用的是它独有的bencode编码。

支持下列类型:字节串、整数、列表和字典。

1.字符串:<字符串的长度>:<字符串的内容>

例如:
announce,编码后为 8:announce
name,编码后为 4:name

2.数字的存储格式: i<十进制整型数>e

例如:
4,编码后为 i4e
1024,编码后为 i1024e

3.列表的存储格式:l<子元素>e
子元素可以是字符串,整数,列表和字典,或者是它们的组合体

例如:
[“name”, “age”, “addr”],编码后为 l4:name3:age4:addre
[1, 2, 3, 4],编码后为 li1ei2ei3ei4ee

4.字典的存储格式:d<关键字><值>e
key只能是经过bencode编码的字符串,value则可以是字符串,整数,列表和字典,或者是它们的组合体,key和value必须是成对出现的

例如:
{ “name” => “Exler”, “age” => “18” },编码后为 d4:name5:Exler3:agei18ee
{“list”=> [1, 2, 3, 4]},编码后为 d4:listli1ei2ei3ei4eee
{“stus”=> [{“name” => “Exler”, “age” => 18}, {“name” => “Jack”, “age” => 19}]},编码后为 d4:stusld4:name5:Exler3:agei18eeeld4:name4:Jack3:agei19eeee
这个稍微有点复杂,拆解下

d
    4:stus
    l
        d
        4:name
        5:Exler
        3:age
        i18e
        e
    e
    l
        d
        4:name
        4:Jack
        3:age
        i19e
        e
    e
e

torrent文件格式

格式有两种类型
• 单文件
• 多文件
单文件结构树形图:

Single-File Torrent
├─announce
├─announce-list
├─comment
├─comment.utf-8
├─creation date
├─encoding
├─info
│ ├─length
│ ├─name
│ ├─name.utf-8
│ ├─piece length
│ ├─pieces
│ ├─publisher
│ ├─publisher-url
│ ├─publisher-url.utf-8
│ └─publisher.utf-8
└─nodes

多文件torrent结构树形图:

Multi-file Torrent
├─announce
├─announce-list
├─comment
├─comment.utf-8
├─creation date
├─encoding
├─info
│ ├─files
│ │ ├─length
│ │ ├─path
│ │ └─path.utf-8
│ ├─name
│ ├─name.utf-8
│ ├─piece length
│ ├─pieces
│ ├─publisher
│ ├─publisher-url
│ ├─publisher-url.utf-8
│ └─publisher.utf-8
└─nodes

• 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:所有关于下载的文件的信息都在这个字段里,它包括多个子字段,而且根据下载的是单个文件还是多个文件,子字段的项目会不同,具体介绍在后面。
• nodes:最后的一个字段是nodes字段,这个字段包含一系列ip和相应端口的列表,是用于连接DHT初始node。

multi-file的info字段是个files列表
files的结构如下:
• lenghth:文件的大小,用byte计算;
• path:文件的名字,在下载时不可更改;
• path.utf-8:文件名的UTF-8编码。

info中其他字段含义如下:
• name:推荐的文件夹名,此项可于下载时更改;
• name.utf-8:推荐的文件夹名的utf-8编码;
• piece length:每个文件块的大小,用Byte计算;
• publisher:文件发布者的名字;
• publisher.utf-8:文件发布者的名字的utf-8编码;
• publisher-url:文件发布者的网址;
• publisher-url.utf-8:文件发布者网址的utf-8编码。

代码解析

解析的主要代码,具体项目的git地址放在文末了

使用github.com/jackpal/bencode-go 这个库进行反序列化的操作

单文件的解析

package torrentfile
import (
    "BTClient/p2p"
    "bytes"
    "github.com/jackpal/bencode-go"
)
type singleTorrent struct {
    // `bencode:""`
    // tracker服务器的URL 字符串
    Announce string `bencode:"announce"`
    // 备用tracker服务器列表 列表
    // 发现 announce-list 后面跟了两个l(ll) announce-listll
    AnnounceList [][]string `bencode:"announce-list"`
    // 种子的创建时间 整数
    CreatDate int64 `bencode:"creation date"`
    // 备注 字符串
    Comment string `bencode:"comment"`
    // 创建者 字符串
    CreatedBy string     `bencode:"created by"`
    Info      singleInfo `bencode:"info"`
    // 包含一系列ip和相应端口的列表,是用于连接DHT初始node
    Nodes [][]interface{} `bencode:"nodes"`
    // 文件的默认编码
    Encoding string `bencode:"encoding"`
    // 备注的utf-8编码
    CommentUtf8 string `bencode:"comment.utf-8"`
}
// 单文件
type singleInfo struct {
    Pieces      string `bencode:"pieces"`
    PieceLength int    `bencode:"piece length"`
    Length      int    `bencode:"length"`
    Name        string `bencode:"name"`
    // 文件发布者
    Publisher string `bencode:"publisher,omitempty"`
    // 文件发布者的网址
    PublisherUrl string `bencode:"publisher-url,omitempty"`
    NameUtf8         string `bencode:"name.utf-8,omitempty"`
    PublisherUtf8    string `bencode:"publisher.utf-8,omitempty"`
    PublisherUrlUtf8 string `bencode:"publisher-url.utf-8,omitempty"`
    MD5Sum           string `bencode:"md5sum,omitempty"`
    Private          bool   `bencode:"private,omitempty"`
}
// 将pieces(以前是一个字符串)拆分为一片哈希(每个[20]byte).以便以后可以轻松访问各个哈希.
// 还计算了整个bencoded infodict(包含名称.大小和片段哈希的dict)的SHA-1哈希.
// 将其称为infohash.在与Tracker服务器和Peer设备对话时.它唯一地标识文件.
// fileParser 单文件解析
func (bto *singleTorrent) fileParser(file []byte) error {
    // 可以进行 反序列化 key value取值
    // fileMetaData, er := bencode.Decode(file)
    // if er != nil {
    // }
    //fmt.Println(fileMetaData)
    err := bencode.Unmarshal(bytes.NewReader(file), &bto)
    return err
}
func (bto *singleTorrent) toTorrentFile() (TorrentFile, error) {
    infoHash, err := hash(bto.Info)
    if err != nil {
        return TorrentFile{}, err
    }
    // 每个分片的 SHA-1 hash 长度是20 把他们从Pieces中切出来
    pieceHashes, err := splitPieceHashes(bto.Info.Pieces)
    if err != nil {
        return TorrentFile{}, err
    }
    tf := TorrentFile{
        Announce: bto.Announce,
        Torrent: p2p.Torrent{
            InfoHash:    infoHash,
            PieceHashes: pieceHashes,
            PieceLength: bto.Info.PieceLength,
            Length:      bto.Info.Length,
            Name:        bto.Info.Name,
        },
    }
    // 添加 备用节点
    tf.AnnounceList = []string{}
    for _, v := range bto.AnnounceList {
        tf.AnnounceList = append(tf.AnnounceList, v[0])
    }
    return tf, nil
}

多文件的解析

package torrentfile
import (
    "BTClient/p2p"
    "bytes"
    "github.com/jackpal/bencode-go"
)
// 多文件 包含5:files
type multipleTorrent struct {
    // `bencode:""`
    // tracker服务器的URL 字符串
    Announce string `bencode:"announce"`
    // 备用tracker服务器列表 列表
    // 发现 announce-list 后面跟了两个l(ll) announce-listll
    AnnounceList [][]string `bencode:"announce-list"`
    // 种子的创建时间 整数
    CreatDate int64 `bencode:"creation date"`
    // 备注 字符串
    Comment string `bencode:"comment"`
    // 创建者 字符串
    CreatedBy string       `bencode:"created by"`
    Info      multipleInfo `bencode:"info"`
    // 包含一系列ip和相应端口的列表,是用于连接DHT初始node
    Nodes [][]interface{} `bencode:"nodes"`
    // 文件的默认编码
    Encoding string `bencode:"encoding"`
    // 备注的utf-8编码
    CommentUtf8 string `bencode:"comment.utf-8"`
}
type multipleInfo struct {
    // 每个块的20个字节的SHA1 Hash的值(二进制格式)
    Pieces string `bencode:"pieces"`
    // 每个块的大小,单位字节 整数
    PieceLength int `bencode:"piece length"`
    // 文件长度 整数
    Length int `bencode:"length,omitempty"`
    // 目录名 字符串
    Name string `bencode:"name"`
    Files []struct {
        // 文件长度 单位字节 整数
        Length int `bencode:"length"`
        // 文件的路径和名字 列表
        Path []string `bencode:"path"`
        // path.utf-8:文件名的UTF-8编码
        PathUtf8 string `bencode:"path.utf-8,omitempty"`
    } `bencode:"files"`
    NameUtf8 string `bencode:"name.utf-8,omitempty"`
}
// multipleParser 多文件解析
func (bto *multipleTorrent) fileParser(file []byte) error {
    err := bencode.Unmarshal(bytes.NewReader(file), &bto)
    return err
}
func (bto *multipleTorrent) toTorrentFile() (TorrentFile, error) {
    infoHash, err := hash(bto.Info)
    if err != nil {
        return TorrentFile{}, err
    }
    // 每个分片的 SHA-1 hash 长度是20 把他们从Pieces中切出来
    pieceHashes, err := splitPieceHashes(bto.Info.Pieces)
    if err != nil {
        return TorrentFile{}, err
    }
    tf := TorrentFile{
        Announce: bto.Announce,
        Torrent: p2p.Torrent{
            InfoHash:    infoHash,
            PieceHashes: pieceHashes,
            PieceLength: bto.Info.PieceLength,
            Length:      bto.Info.Length,
            Name:        bto.Info.Name,
        },
    }
    // 添加 备用节点
    tf.AnnounceList = []string{}
    for _, v := range bto.AnnounceList {
        tf.AnnounceList = append(tf.AnnounceList, v[0])
    }
    // 构建 fileInfo 列表
    var fileInfo []p2p.FileInfo
    for _, v := range bto.Info.Files {
        path := ""
        for _, p := range v.Path {
            path += "/" + p
        }
        fileInfo = append(fileInfo, p2p.FileInfo{
            Path:   path,
            Length: v.Length,
        })
    }
    tf.File = fileInfo
    return tf, nil
}

全部代码的Gitee

这是torrent文件解析的部分,后期会更新种子文件下载的内容。

参考了下面的链接,不过这位大神只做了单文件下载,没有做多文件的,只支持TCP协议。

参考
原项目地址
博客
中文

 类似资料: