- Golang ------ torrent檔案解析
- Golang ------ torrent檔案下載下傳 (1)
- 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協定。
參考
原項目位址
部落格
中文