天天看點

Golang ------ torrent檔案解析bencode編碼torrent檔案格式代碼解析

  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協定。

參考

原項目位址

部落格

中文