Dictionary<string, object>类型的编码格式为d[Key-Value Pair]e,以d开头,以e结尾。示例:
Dictionary<{"name":"create chen"},{"age":23}> => d4:name11:create chen3:agei23ee
List<object>类型
List<object>类型的编码格式为l[object]e,以l开头,以e结尾。示例:
List<"abc", 123> => l3:abci123ee Bencode实现 编码 public static string Encode(object obj) { var sb = new StringBuilder(); if(obj is Dictionary<string,object>) { var parseObj = obj as Dictionary<string, object>; sb.Append("d"); foreach (var o in parseObj) { sb.AppendFormat("{0}:{1}{2}", o.Key.Length,o.Key, Encode(o.Value)); } sb.Append("e"); } if ((obj as int?) != null) { var parseObj = (int) obj; sb.AppendFormat("i{0}e", parseObj); } if (obj is List<object>) { var parseObj = obj as List<object>; sb.Append("l"); foreach (var o in parseObj) { sb.Append(Encode(o)); } sb.Append("e"); } if (obj is string) { var parseObj = obj as string; sb.AppendFormat("{0}:{1}", parseObj.Length, parseObj); } return sb.ToString(); } 解码 public static object Decode(string s) { return DecodeObject(s, ref _index, EncodeState.Value); } private enum EncodeState { Key, Value } private static int _index; private static object DecodeObject(string str,ref int index, EncodeState state) { var obj = new Dictionary<string, object>(); var c = str[index]; while (c != 'e') { if (c == 'd') { index++; return DecodeObject(str, ref index,EncodeState.Key); } if (c == 'i') { var value = ""; index++; c = str[index]; while (c != 'e') { value += c.ToString(CultureInfo.InvariantCulture); index++; c = str[index]; } return Convert.ToInt32(value); } if (c == 'l') { index++; var value = new List<object>(); while (str[index]!='e') { value.Add(DecodeObject(str, ref index, EncodeState.Value)); index++; } return value; } if ('0' < c && c <= '9') { string strLength = ""; while (c != ':') { strLength += c.ToString(CultureInfo.InvariantCulture); c = str[++index]; } var length = Convert.ToInt32(strLength); var strContent = ""; for (int i = 0; i < length; i++) { strContent += str[index + 1].ToString(CultureInfo.InvariantCulture); index++; } if (state == EncodeState.Value) { return strContent; } index++; obj.Add(strContent, DecodeObject(str, ref index, EncodeState.Value)); state = EncodeState.Key; index++; } c = str[index]; } return obj; } 编写项目这里使用Go来编写,也是首次使用Go完成网络工具。仅包含主要代码,完整项目见Github。
寻找 解析种子(~/torrentfile/torrentfile.go) import ( "github.com/jackpal/bencode-go" )这里省略了自带库文件的导入。
type bencodeInfo struct { Pieces string `bencode:"pieces"` PieceLength int `bencode:"piece length"` Length int `bencode:"length"` Name string `bencode:"name"` } type bencodeTorrent struct { Announce string `bencode:"announce"` Info bencodeInfo `bencode:"info"` } // Open函数用于解析种子 func Open(path string) (TorrentFile, error) { file, err := os.Open(path) if err != nil { return TorrentFile{}, err } defer file.Close() bto := bencodeTorrent{} err = bencode.Unmarshal(file, &bto) if err != nil { return TorrentFile{}, err } return bto.toTorrentFile() }处理时,将pieces对应的值(原先为哈希值的字符串)变成哈希值切片(每个长度为20 bytes),以便后续调用每个独立的哈希值。另外,计算info对应的整个字典(含有名称、大小、文件块哈希值)的SHA-1哈希值,存储在infohash,在与trackers服务器和peers主机交互时表示所需的文件。
type TorrentFile struct { Announce string InfoHash [20]byte PieceHashes [][20]byte PieceLength int Length int Name string } func (bto bencodeTorrent) toTorrentFile() (TorrentFile, error) { // ... } 从trackers服务器获取peers主机地址(~/torrentfile/tracker.go)处理完种子后,就可以向trackers服务器发起请求:作为一台peer主机,需要获取同一网络中的其它peers主机的列表。只需要对announce对应URL发起GET请求(需要设置几个请求参数)。
// buildTrackerURL函数用于构成请求peers列表的序列 func (t * TorrentFile) buildTrackerURL(peerID [20]byte, port uint16) (string, error) { base, err:= url.Parse(t.Announce) if err != nil { return "", err } params := url.Values{ "info_hash": []string{string(t.InfoHash[:])}, "peer_id": []string{string(peerID[:])}, "port": []string{strconv.Itoa(int(port))}, "uploaded": []string{"0"}, "downloaded": []string{"0"}, "compact": []string{"1"}, "left": []string{strconv.Itoa(t.Length)}, } base.RawQuery = params.Encode() return base.String(), nil }其中重要的参数有: