bittorrent 学习(一) 种子文件分析与bitmap位图

终于抽出时间来进行 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] = '';
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] = '';
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
",p->announce);
63         p = p->next;
64     }
65 #endif    
66     
67     return 0;
68 }
View Code

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
",piece_length);
18 #endif
19 
20     return 0;
21 }
View Code

 分析文件最常用的就是寻找关键字 代码采用比较简单的方法,逐个字节比较关键字

 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 }
View Code

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("
");
57 #endif
58 
59     return 0;
60 }
View Code

我们需要为自己生成一个用于辨识的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("
");
13 #endif
14 
15     return 0;
16 }
View Code

代码中使用了堆内存,在退出或者不使用的时候需要回收。调用 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 }
View Code

//=====================================================================================================

下面看下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;
View Code

创建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
"); 
 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
"); 
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 }
View Code

 根据索引获取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 }
View Code

 

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 }
View Code

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 }
View Code

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 }
View Code

 把代码改写成了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
", __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 
", begin, end);
115 
116 #if 1
117     printf("info_hash:");
118     for (i = 0; i < 20; i++)
119         printf("%.2x ", infoHash[i]);
120     printf("
");
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
",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
",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
", 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] = '';
283     }
284     else {
285         return b;
286     }
287 
288 #if 1
289     printf("get_pieces ok
");
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
", 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("
");
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  及代码

作 者: itdef
欢迎转帖 请保持文本完整并注明出处
技术博客 http://www.cnblogs.com/itdef/
B站算法视频题解
https://space.bilibili.com/18508846
qq 151435887
gitee https://gitee.com/def/
欢迎c c++ 算法爱好者 windows驱动爱好者 服务器程序员沟通交流
如果觉得不错,欢迎点赞,你的鼓励就是我的动力
阿里打赏 微信打赏
原文地址:https://www.cnblogs.com/itdef/p/9740797.html