终于抽出时间来进行 BITTORRENT的学习了
BT想必大家都很熟悉了,是一种文件分发协议。每个下载者在下载的同时也在向其他下载者分享文件。
相对于FTP HTTP协议,BT并不是从某一个或者几个指定的点进行文件下载,而是用户之间进行交互,每个用户既是下载者也是上传者.
BT并不会出现提供下载的服务点出现问题而无法下载的现象。
我尝试从BT文件开始下载的流程来分析下我们需要那些功能。
首先我们从某网站下载BT种子文件,文件很小,记录要下载的实际文件的一些信息。
那么我们就需要从该BT种子文件解析出 文件的数量(比如视频文件和文件字幕等多个文件),文件名称,文件大小,还有最重要的连接何处网站获取其他用户信息等等等等。
这个就是种子解析模块。
Tracker服务器会记录在下载该文件的ip和端口,我们连接上去就可以从其他用户peer下载文件了,同时Tracker服务器也会记录我们自己的IP和端口,为其他peer分享文件。
这个是连接Tracker模块。
我们与其他peer进行连接,交换文件数据。就是peer交换数据模块。
主体就是这些。那么在实际运行中,会有一些细节需要解决,衍生出次级模块。
比如我们要知道其他peer下载的文件内容进度和提供我们下载文件的内容进度,这就需要bitmap管理模块。
为了防止有的peer之下载不上传,就需要添加一个策略管理,鼓励所有peer踊跃分享文件。
我们不可能每下一点点文件内容就马上写入磁盘,这样效率太低,所以也需要缓冲管理模块。
以及整个流程中消息的流转和管理的,消息管理模块。
结构图如下:
今天看看种子文件解析代码.bt种子文件使用B编码。如图
了解了字符串 数字 列表和字典后,看看一个实际的BT文件
最开始的就是 d8:announce 41:http://tracker.trackerfix.com:80/announce
13:announce-list
l
l
41:http://tracker.trackerfix.com:80/announce
e
l
30:udp://9.rarbg.to:2710/announce
e
。。。。。。。
e
字典有两个 映射 一个key value是 announce 和 http://tracker.trackerfix.com:80/announce
一个key value 是 announce-list 对应一组列表 列表是 http://tracker.trackerfix.com:80/announce udp://9.rarbg.to:2710/announce 等等
announce-list 中包含了 announce项目下的tracker服务器IP和端口 所以代码中只需要搜索其中一个关键字即可
1 int read_announce_list()
2 {
3 Announce_list *node = NULL;
4 Announce_list *p = NULL;
5 int len = 0;
6 long i;
7
8 if( find_keyword("13:announce-list",&i) == 0 ) {
9 if( find_keyword("8:announce",&i) == 1 ) {
10 i = i + strlen("8:announce");
11 while( isdigit(metafile_content[i]) ) {
12 len = len * 10 + (metafile_content[i] - '0');
13 i++;
14 }
15 i++; // 跳过 ':'
16
17 node = (Announce_list *)malloc(sizeof(Announce_list));
18 strncpy(node->announce,&metafile_content[i],len);
19 node->announce[len] = '\0';
20 node->next = NULL;
21 announce_list_head = node;
22 }
23 }
24 else { // 如果有13:announce-list关键词就不用处理8:announce关键词
25 i = i + strlen("13:announce-list");
26 i++; // skip 'l'
27 while(metafile_content[i] != 'e') {
28 i++; // skip 'l'
29 while( isdigit(metafile_content[i]) ) {
30 len = len * 10 + (metafile_content[i] - '0');
31 i++;
32 }
33 if( metafile_content[i] == ':' ) i++;
34 else return -1;
35
36 // 只处理以http开头的tracker地址,不处理以udp开头的地址
37 if( memcmp(&metafile_content[i],"http",4) == 0 ) {
38 node = (Announce_list *)malloc(sizeof(Announce_list));
39 strncpy(node->announce,&metafile_content[i],len);
40 node->announce[len] = '\0';
41 node->next = NULL;
42
43 if(announce_list_head == NULL)
44 announce_list_head = node;
45 else {
46 p = announce_list_head;
47 while( p->next != NULL) p = p->next; // 使p指向最后个结点
48 p->next = node; // node成为tracker列表的最后一个结点
49 }
50 }
51
52 i = i + len;
53 len = 0;
54 i++; // skip 'e'
55 if(i >= filesize) return -1;
56 }
57 }
58
59 #ifdef DEBUG
60 p = announce_list_head;
61 while(p != NULL) {
62 printf("%s\n",p->announce);
63 p = p->next;
64 }
65 #endif
66
67 return 0;
68 }
piece length 表示每个piece的长度 一般是128K
1 int get_piece_length()
2 {
3 long i;
4
5 if( find_keyword("12:piece length",&i) == 1 ) {
6 i = i + strlen("12:piece length"); // skip "12:piece length"
7 i++; // skip 'i'
8 while(metafile_content[i] != 'e') {
9 piece_length = piece_length * 10 + (metafile_content[i] - '0');
10 i++;
11 }
12 } else {
13 return -1;
14 }
15
16 #ifdef DEBUG
17 printf("piece length:%d\n",piece_length);
18 #endif
19
20 return 0;
21 }
分析文件最常用的就是寻找关键字 代码采用比较简单的方法,逐个字节比较关键字
1 int find_keyword(char *keyword,long *position)
2 {
3 long i;
4
5 *position = -1;
6 if(keyword == NULL) return 0;
7
8 for(i = 0; i < filesize-strlen(keyword); i++) {
9 if( memcmp(&metafile_content[i], keyword, strlen(keyword)) == 0 ) {
10 *position = i;
11 return 1;
12 }
13 }
14
15 return 0;
16 }
get_info_hash() 计算的是piece的哈希值
首先在文件中找到"4:info"关键字,找到其后的info信息 进行哈希计算.遇到需要‘e’字母对应的开头(比如字典开头‘d’,列表开头'l',数字开头'i'),计数加1.遇到‘e’,计数减1。
计数到零,则说明找到"4:info"的完整信息,可以开始进行哈希计算。
这个要比网络上一些 查找 "4:info" 到 "5:nodes"之间字符串要可靠得多 有些种子文件是没有"5:nodes"
1 int get_info_hash()
2 {
3 int push_pop = 0;
4 long i, begin, end;
5
6 if(metafile_content == NULL) return -1;
7
8 if( find_keyword("4:info",&i) == 1 ) {
9 begin = i+6; // begin是关键字"4:info"对应值的起始下标
10 } else {
11 return -1;
12 }
13
14 i = i + 6; // skip "4:info"
15 for(; i < filesize; )
16 if(metafile_content[i] == 'd') {
17 push_pop++;
18 i++;
19 } else if(metafile_content[i] == 'l') {
20 push_pop++;
21 i++;
22 } else if(metafile_content[i] == 'i') {
23 i++; // skip i
24 if(i == filesize) return -1;
25 while(metafile_content[i] != 'e') {
26 if((i+1) == filesize) return -1;
27 else i++;
28 }
29 i++; // skip e
30 } else if((metafile_content[i] >= '0') && (metafile_content[i] <= '9')) {
31 int number = 0;
32 while((metafile_content[i] >= '0') && (metafile_content[i] <= '9')) {
33 number = number * 10 + metafile_content[i] - '0';
34 i++;
35 }
36 i++; // skip :
37 i = i + number;
38 } else if(metafile_content[i] == 'e') {
39 push_pop--;
40 if(push_pop == 0) { end = i; break; }
41 else i++;
42 } else {
43 return -1;
44 }
45 if(i == filesize) return -1;
46
47 SHA1_CTX context;
48 SHA1Init(&context);
49 SHA1Update(&context, &metafile_content[begin], end-begin+1);
50 SHA1Final(info_hash, &context);
51
52 #ifdef DEBUG
53 printf("info_hash:");
54 for(i = 0; i < 20; i++)
55 printf("%.2x ",info_hash[i]);
56 printf("\n");
57 #endif
58
59 return 0;
60 }
我们需要为自己生成一个用于辨识的peerid,调用get_peer_id()
1 int get_peer_id()
2 {
3 // 设置产生随机数的种子
4 srand(time(NULL));
5 // 生成随机数,并把其中12位赋给peer_id,peer_id前8位固定为-TT1000-
6 sprintf(peer_id,"-TT1000-%12d",rand());
7
8 #ifdef DEBUG
9 int i;
10 printf("peer_id:");
11 for(i = 0; i < 20; i++) printf("%c",peer_id[i]);
12 printf("\n");
13 #endif
14
15 return 0;
16 }
代码中使用了堆内存,在退出或者不使用的时候需要回收。调用 release_memory_in_parse_metafile()
1 void release_memory_in_parse_metafile()
2 {
3 Announce_list *p;
4 Files *q;
5
6 if(metafile_content != NULL) free(metafile_content);
7 if(file_name != NULL) free(file_name);
8 if(pieces != NULL) free(pieces);
9
10 while(announce_list_head != NULL) {
11 p = announce_list_head;
12 announce_list_head = announce_list_head->next;
13 free(p);
14 }
15
16 while(files_head != NULL) {
17 q = files_head;
18 files_head = files_head->next;
19 free(q);
20 }
21 }
//=====================================================================================================
下面看下bitmap 位图
位图相当于一个文件的缩略图,一个字节有8位,如果每位的01代表一个文件的10k的空间是否下载成功,那么我们使用一个字节就可以表示80K文件的下载进度。
而实际上在bttorrent中,每位使用01表示一个piece的下载成功与否,若一个piece是256k,那么一个字节8位就可以表示 256*8=2048k=2M文件的下载进度。
Bitmap结构如下
1 typedef struct _Bitmap {
2 unsigned char *bitfield; // 保存位图
3 int bitfield_length; // 位图所占的总字节数
4 int valid_length; // 位图有效的总位数,每一位代表一个piece
5 } Bitmap;
创建bitmap函数流程如下
首先分配Bitmap的内存,然后根据piece长度决定bitmap记录的长度。
valid_length是有效长度,就是能表示的真实文件的长度。 一个位图表示piece长度的1/20
bitfield_length就是位图占用的长度。 一个位图表示piece长度的1/20再除以8 ,就是字节长度
然后根据bitfield_length分配内存。这里需要注意的是,文件长度未必就是完全可以整除的长度,那么bitfield_length就在添加一个字节,用于指示文件整除后不足以显示的余额
1 // 如果存在一个位图文件,则读位图文件并把获取的内容保存到bitmap
2 // 如此一来,就可以实现断点续传,即上次下载的内容不至于丢失
3 int create_bitfield()
4 {
5 bitmap = (Bitmap *)malloc(sizeof(Bitmap));
6 if(bitmap == NULL) {
7 printf("allocate memory for bitmap fiailed\n");
8 return -1;
9 }
10
11 // pieces_length除以20即为总的piece数
12 bitmap->valid_length = pieces_length / 20;
13 bitmap->bitfield_length = pieces_length / 20 / 8;
14 if( (pieces_length/20) % 8 != 0 ) bitmap->bitfield_length++;
15
16 bitmap->bitfield = (unsigned char *)malloc(bitmap->bitfield_length);
17 if(bitmap->bitfield == NULL) {
18 printf("allocate memory for bitmap->bitfield fiailed\n");
19 if(bitmap != NULL) free(bitmap);
20 return -1;
21 }
22
23 char bitmapfile[64];
24 sprintf(bitmapfile,"%dbitmap",pieces_length);
25
26 int i;
27 FILE *fp = fopen(bitmapfile,"rb");
28 if(fp == NULL) { // 若打开文件失败,说明开始的是一个全新的下载
29 memset(bitmap->bitfield, 0, bitmap->bitfield_length);
30 } else {
31 fseek(fp,0,SEEK_SET);
32 for(i = 0; i < bitmap->bitfield_length; i++)
33 (bitmap->bitfield)[i] = fgetc(fp);
34 fclose(fp);
35 // 给download_piece_num赋新的初值
36 download_piece_num = get_download_piece_num();
37 }
38
39 return 0;
40 }
根据索引获取bitmap的标识值
因为是每1位代表一个pieces的下载与否
索引输入的索引值index是位的个数
index / 8 = i i就代表查询或者设置的那位在 第i个byte中。
但是byte有8位,具体是要查询或者设置哪一位呢? index%8=j j就是我们要查询设置的位
示意图 index从1开始
1 int get_bit_value(Bitmap *bitmap,int index)
2 {
3 int ret;
4 int byte_index;
5 unsigned char byte_value;
6 unsigned char inner_byte_index;
7
8 if(index >= bitmap->valid_length) return -1;
9
10 byte_index = index / 8;
11 byte_value = bitmap->bitfield[byte_index];
12 inner_byte_index = index % 8;
13
14 byte_value = byte_value >> (7 - inner_byte_index);
15 if(byte_value % 2 == 0) ret = 0;
16 else ret = 1;
17
18 return ret;
19 }
20
21 int set_bit_value(Bitmap *bitmap,int index,unsigned char v)
22 {
23 int byte_index;
24 unsigned char inner_byte_index;
25
26 if(index >= bitmap->valid_length) return -1;
27 if((v != 0) && (v != 1)) return -1;
28
29 byte_index = index / 8;
30 inner_byte_index = index % 8;
31
32 v = v << (7 - inner_byte_index);
33 bitmap->bitfield[byte_index] = bitmap->bitfield[byte_index] | v;
34
35 return 0;
36 }
int all_zero(Bitmap *bitmap)
int all_set(Bitmap *bitmap) 将bitmap记录全部置0和置1
1 int all_zero(Bitmap *bitmap)
2 {
3 if(bitmap->bitfield == NULL) return -1;
4 memset(bitmap->bitfield,0,bitmap->bitfield_length);
5 return 0;
6 }
7
8 int all_set(Bitmap *bitmap)
9 {
10 if(bitmap->bitfield == NULL) return -1;
11 memset(bitmap->bitfield,0xff,bitmap->bitfield_length);
12 return 0;
13 }
is_interested(Bitmap *dst,Bitmap *src) 比较两个bitmap
如果src的bitmap中有1位为0(即没有这个piece)
而dst的bitmap中这1位为1(即有这个piece) 则说明 src对dst感兴趣 interest
1 int is_interested(Bitmap *dst,Bitmap *src)
2 {
3 unsigned char const_char[8] = { 0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};
4 unsigned char c1, c2;
5 int i, j;
6
7 if( dst==NULL || src==NULL ) return -1;
8 if( dst->bitfield==NULL || src->bitfield==NULL ) return -1;
9 if( dst->bitfield_length!=src->bitfield_length ||
10 dst->valid_length!=src->valid_length )
11 return -1;
12
13 for(i = 0; i < dst->bitfield_length-1; i++) {
14 for(j = 0; j < 8; j++) {
15 c1 = (dst->bitfield)[i] & const_char[j];
16 c2 = (src->bitfield)[i] & const_char[j];
17 if(c1>0 && c2==0) return 1;
18 }
19 }
20
21 j = dst->valid_length % 8;
22 c1 = dst->bitfield[dst->bitfield_length-1];
23 c2 = src->bitfield[src->bitfield_length-1];
24 for(i = 0; i < j; i++) {
25 if( (c1&const_char[i])>0 && (c2&const_char[i])==0 )
26 return 1;
27 }
28
29 return 0;
30 }
get_download_piece_num() 获取位图中为1的位数 也就是下载了多少pieces
直接和 0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01 相与
这种做法是遍历一次查询多少个1 要快很多
1 int get_download_piece_num()
2 {
3 unsigned char const_char[8] = { 0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};
4 int i, j;
5
6 if(bitmap==NULL || bitmap->bitfield==NULL) return 0;
7
8 download_piece_num =0;
9
10 for(i = 0; i < bitmap->bitfield_length-1; i++) {
11 for(j = 0; j < 8; j++) {
12 if( ((bitmap->bitfield)[i] & const_char[j]) != 0)
13 download_piece_num++;
14 }
15 }
16
17 unsigned char c = (bitmap->bitfield)[i]; // c存放位图最后一个字节
18 j = bitmap->valid_length % 8; // j是位图最后一个字节的有效位数
19 for(i = 0; i < j; i++) {
20 if( (c & const_char[i]) !=0 ) download_piece_num++;
21 }
22
23 return download_piece_num;
24 }
把代码改写成了cpp
附上
1 #pragma once
2 #include "pre.h"
3 #include <string>
4 #include <vector>
5
6 NAMESPACE(DEF)
7 NAMESPACE(BTPARSE)
8 class ParseBT {
9 public:
10 ParseBT() {
11 metaFileSize = 0;
12 piece_length = -1;
13 pieces_length = 0;
14 multi_file = false;
15 buf_ptr = std::shared_ptr<char>(new char[DEF_BUF_SIZE], std::default_delete<char[]>());
16 }
17 ~ParseBT() {}
18 bool ReadMetaFile(std::string name);
19 bool ReadAnnounceList();
20 bool FindKeyWord(const std::string& key,int& pos);
21 bool IsMultiFiles();
22 bool GetPieceLength();
23 bool GetPieces();
24 bool GetFileName();
25 bool GetFilesLengthPath();
26 bool GetFileLength();
27 bool GetInfoHash();
28 bool GetPerID();
29 private:
30 long metaFileSize;
31 bool multi_file;
32 int piece_length;
33 int pieces_length;
34 ParseBT(const ParseBT&) = delete;
35 ParseBT& operator=(const ParseBT&) = delete;
36 enum {
37 DEF_BUF_SIZE = 1024 * 100
38 };
39
40 unsigned char infoHash[20];
41 unsigned char peerID[20];
42 std::vector < std::pair<std::string, size_t> > fileInfos;
43
44 std::shared_ptr<char> buf_ptr;
45 std::shared_ptr<char> pieces_ptr;
46 std::vector<std::string> announce_list;
47 };
48
49
50
51
52
53
54
55
56
57
58
59 ENDNAMESPACE(BTPARSE)
60 ENDNAMESPACE(DEF)
ParseBT.h
1 #include <iostream>
2
3 #include <time.h>
4 extern "C" {
5 #include "sha1.h"
6 }
7 #include "ParseBT.h"
8
9
10 NAMESPACE(DEF)
11 NAMESPACE(BTPARSE)
12 struct Fclose
13 {
14 void operator()(FILE* fp)
15 {
16 fclose(fp);
17 fp = NULL;
18 }
19 };
20
21 bool ParseBT::ReadMetaFile(std::string name) {
22 bool b = false;
23 if (name.empty())
24 return b;
25
26 std::unique_ptr<FILE, Fclose> fp(fopen(name.c_str(), "rb"));
27 if (fp == nullptr) {
28 std::cerr << __FUNCTION__ << "error!" << std::endl;
29 return b;
30 }
31
32 // 获取种子文件的长度
33 fseek(fp.get(), 0, SEEK_END);
34 metaFileSize = ftell(fp.get());
35 if (metaFileSize == -1) {
36 printf("%s:%d fseek failed\n", __FILE__, __LINE__);
37 return b;
38 }
39 if (DEF_BUF_SIZE < metaFileSize) {
40 std::shared_ptr<char> p = std::shared_ptr<char>(new char[metaFileSize], std::default_delete<char[]>());
41 buf_ptr.swap( p );
42 }
43
44 fseek(fp.get(), 0, SEEK_SET);
45 int readbyte = fread(buf_ptr.get(),1, metaFileSize,fp.get());
46 if (readbyte != metaFileSize) {
47 std::cerr << __FUNCTION__ << ". fread() error!" << std::endl;
48 return b;
49 }
50
51 b = true;
52 return b;
53 }
54
55 bool ParseBT::GetInfoHash() {
56 bool b = false;
57 int i = 0;
58 int begin = 0; int push_pop = 0; int end = 0;
59
60 if (buf_ptr == NULL) return b;
61
62 if (FindKeyWord("4:info", i) == true) {
63 begin = i + 6; // begin是关键字"4:info"对应值的起始下标
64 }
65 else {
66 return -b;
67 }
68
69 i = i + 6; // skip "4:info"
70
71 for (; i < metaFileSize; )
72 if (buf_ptr.get()[i] == 'd') {
73 push_pop++;
74 i++;
75 }
76 else if (buf_ptr.get()[i] == 'l') {
77 push_pop++;
78 i++;
79 }
80 else if (buf_ptr.get()[i] == 'i') {
81 i++; // skip i
82 if (i == metaFileSize) return -1;
83 while (buf_ptr.get()[i] != 'e') {
84 if ((i + 1) == metaFileSize) return -1;
85 else i++;
86 }
87 i++; // skip e
88 }
89 else if ((buf_ptr.get()[i] >= '0') && (buf_ptr.get()[i] <= '9')) {
90 int number = 0;
91 while ((buf_ptr.get()[i] >= '0') && (buf_ptr.get()[i] <= '9')) {
92 number = number * 10 + buf_ptr.get()[i] - '0';
93 i++;
94 }
95 i++; // skip :
96 i = i + number;
97 }
98 else if (buf_ptr.get()[i] == 'e') {
99 push_pop--;
100 if (push_pop == 0) { end = i; break; }
101 else i++;
102 }
103 else {
104 return -1;
105 }
106 if (i == metaFileSize) return b;
107
108 SHA1Context context;
109 SHA1Reset(&context);
110 unsigned char* p = (unsigned char*)buf_ptr.get();
111 SHA1Input(&context, &(p[begin]), end - begin + 1);
112 SHA1Result(&context, infoHash);
113
114 printf("begin = %d ,end = %d \n", begin, end);
115
116 #if 1
117 printf("info_hash:");
118 for (i = 0; i < 20; i++)
119 printf("%.2x ", infoHash[i]);
120 printf("\n");
121 #endif
122
123 b = true;
124 return b;
125 }
126
127
128 bool ParseBT::GetFileName() {
129 bool b = false;
130 int i;
131 int count = 0;
132
133 if (FindKeyWord("4:name", i) == true) {
134 i = i + 6; // skip "4:name"
135 while ((buf_ptr.get())[i] != ':') {
136 count = count * 10 + ((buf_ptr.get())[i] - '0');
137 i++;
138 }
139 i++; // skip ':'
140 std::string file_name(&(buf_ptr.get())[i], &(buf_ptr.get())[i]+count);
141 //std::string s = "反贪风暴3.L.Storm.2018.1080p.WEB-DL.X264.AAC-国粤中字-RARBT";
142 }
143 else {
144 return b;
145 }
146
147 #if 1
148 // 由于可能含有中文字符,因此可能打印出乱码
149 // printf("file_name:%s\n",file_name);
150 #endif
151
152 return b;
153 }
154
155 bool ParseBT::FindKeyWord(const std::string& key, int& pos) {
156 bool b = false;
157 pos = 0;
158 if (key.empty()) return b;
159
160 for (int i = 0; i < metaFileSize - key.size(); i++) {
161 if (memcmp(&(buf_ptr.get())[i], key.c_str(),key.size()) == 0) {
162 pos = i; b = true;
163 return b;
164 }
165 }
166
167 return b;
168 }
169
170 bool ParseBT::ReadAnnounceList() {
171 bool b = false;
172 int i = -1;
173 int len = 0;
174 if (FindKeyWord("13:announce-list", i) == false) {
175 if (FindKeyWord("8:announce", i) == true) {
176 i = i + strlen("8:announce");
177 while (isdigit((buf_ptr.get())[i])) {
178 len = len * 10 + ((buf_ptr.get())[i] - '0');
179 i++;
180 }
181 i++; // 跳过 ':'
182
183 std::string s ( &(buf_ptr.get())[i] , &(buf_ptr.get())[i]+len);
184 announce_list.push_back(s);
185 b = true;
186 }
187 }
188 else {
189 //如果有13:announce-list关键词就不用处理8:announce关键词
190 i = i + strlen("13:announce-list");
191 i++; // skip 'l'
192 while ((buf_ptr.get())[i] != 'e') {
193 i++; // skip 'l'
194 while (isdigit((buf_ptr.get())[i])) {
195 len = len * 10 + ((buf_ptr.get())[i] - '0');
196 i++;
197 }
198 if ((buf_ptr.get())[i] == ':') i++;
199 else return b;
200
201 // 只处理以http开头的tracker地址,不处理以udp开头的地址
202 if (memcmp(&(buf_ptr.get())[i], "http", 4) == 0) {
203
204 std::string s(&(buf_ptr.get())[i], &(buf_ptr.get())[i] + len);
205 announce_list.push_back(s);
206 }
207
208 i = i + len;
209 len = 0;
210 i++; // skip 'e'
211 if (i >= metaFileSize) return b;
212
213 }
214 }
215 #if 0
216 std::cout << "announce_list size = " << announce_list.size() << std::endl;
217 for (auto& e : announce_list) {
218 std::cout << e << std::endl;
219 }
220 std::cout << std::endl;
221 #endif
222 b = true;
223 return b;
224 }
225
226 bool ParseBT::IsMultiFiles() {
227 bool b = false;
228 int i;
229
230 if (FindKeyWord("5:files", i) == true) {
231 multi_file = true;
232 b = true;
233 }
234
235 #if 1
236 printf("is_multi_files:%d\n",multi_file);
237 #endif
238
239 b = true;
240 return b;
241 }
242
243 bool ParseBT::GetPieceLength() {
244 int length = 0;
245 int i = 0;
246 if (FindKeyWord("12:piece length", i) == true) {
247 i = i + strlen("12:piece length"); // skip "12:piece length"
248 i++; // skip 'i'
249 while ((buf_ptr.get())[i] != 'e') {
250 length = length * 10 + ((buf_ptr.get())[i] - '0');
251 i++;
252 }
253 }
254 else {
255 return false;
256 }
257
258 piece_length = length;
259
260 #if 1
261 printf("piece length:%d\n", piece_length);
262 #endif
263
264 return true;
265 }
266
267 bool ParseBT::GetPieces() {
268 bool b = false;
269 int i = 0;
270
271 if (FindKeyWord("6:pieces", i) == true) {
272 i = i + 8; // skip "6:pieces"
273 while ((buf_ptr.get())[i] != ':') {
274 pieces_length = pieces_length * 10 + ((buf_ptr.get())[i] - '0');
275 i++;
276 }
277 i++; // skip ':'
278
279 pieces_ptr = std::shared_ptr<char>(new char[pieces_length + 1], std::default_delete<char[]>());
280
281 memcpy(pieces_ptr.get(), &(buf_ptr.get())[i], pieces_length);
282 (pieces_ptr.get())[pieces_length] = '\0';
283 }
284 else {
285 return b;
286 }
287
288 #if 1
289 printf("get_pieces ok\n");
290 #endif
291
292 b = true;
293 return b;
294 }
295
296
297 bool ParseBT::GetFileLength() {
298 bool b = false;
299 int i = 0;
300 size_t file_length = 0;
301 if (IsMultiFiles() == true) {
302 if (fileInfos.empty())
303 GetFilesLengthPath();
304 for (auto& e : fileInfos) {
305 file_length += e.second;
306 }
307 }
308 else {
309 if (FindKeyWord("6:length", i) == true) {
310 i = i + 8;
311 i++;
312 while (buf_ptr.get()[i] != 'e') {
313 file_length = file_length * 10 + (buf_ptr.get()[i] -'0');
314 i++;
315 }
316 }
317 }
318
319 #if 1
320 printf("file_length:%lld\n", file_length);
321 #endif
322
323 b = true;
324
325 return b;
326 }
327
328
329 bool ParseBT::GetFilesLengthPath() {
330 bool b = false;
331 if (IsMultiFiles() != true) {
332 return b;
333 }
334
335 std::string name;
336 size_t length = 0;
337 int i = 0;
338 int count = 0;
339 for ( i = 0; i < metaFileSize - 8; i++) {
340 if (memcmp(&(buf_ptr.get())[i], "6:length", 8) == 0) {
341 i = i + 8;
342 i++;
343
344 while ((buf_ptr.get())[i] != 'e') {
345 length = length*10 + ((buf_ptr.get())[i] - '0');
346 i++;
347 }
348 }
349
350 if (memcmp(&(buf_ptr.get())[i], "4:path", 6) == 0) {
351 i = i + 6;
352 i++;
353 count = 0;
354 while (buf_ptr.get()[i] != ':') {
355 count = count * 10 + (buf_ptr.get()[i] - '0');
356 i++;
357 }
358 i++;
359 name = std::string(&(buf_ptr.get())[i], &(buf_ptr.get())[i] + count);
360 //std::cout << name << std::endl;
361
362 if (!name.empty() && length != 0) {
363 std::pair<std::string, size_t> pa{ name,length };
364 fileInfos.push_back(pa);
365 name.clear();
366 length = 0;
367 }
368 }
369 }
370
371 b = true;
372 return b;
373 }
374
375 bool ParseBT::GetPerID() {
376 bool b = false;
377 srand(time(NULL));
378 sprintf((char*)peerID, "TT1000-%12d", rand());
379
380 #if 1
381 int i;
382 printf("peer_id:");
383 for (i = 0; i < 20; i++) printf("%c", peerID[i]);
384 printf("\n");
385 #endif
386
387 b = true;
388 return b;
389 }
390
391
392
393
394 ENDNAMESPACE(BTPARSE)
395 ENDNAMESPACE(DEF)
ParseBT.cpp
1 // MyParseBTFile.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
2 //
3
4
5 #include <iostream>
6 #include "ParseBT.h"
7
8 using namespace DEF::BTPARSE;
9
10 int main()
11 {
12 for (int i = 0; i < 100000; i++) {
13 ParseBT pbt;
14 if (false == pbt.ReadMetaFile("1.torrent"))
15 return 1;
16 int pos = -1;
17 pbt.FindKeyWord("info", pos);
18 pbt.ReadAnnounceList();
19 pbt.IsMultiFiles();
20 pbt.GetPieceLength();
21 pbt.GetPieces();
22 pbt.GetFileName();
23 pbt.GetFilesLengthPath();
24 pbt.GetFileLength();
25 pbt.GetInfoHash();
26 pbt.GetPerID();
27 }
28
29
30 }
main.cpp
参考
《linux c编程实战》第十三章节btcorrent 及代码