• 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驱动爱好者 服务器程序员沟通交流
    如果觉得不错,欢迎点赞,你的鼓励就是我的动力
    阿里打赏 微信打赏
  • 相关阅读:
    180602-nginx多域名配置
    180601-MySql性能监控工具MyTop
    180530-反射获取泛型类的实际参数
    180531-Spring中JavaConfig知识小结
    RabbitMQ基础教程之Spring&JavaConfig使用篇
    RabbitMQ基础教程之使用进阶篇
    RabbitMQ基础教程之基本使用篇
    jquery控制文字内容溢出用点点点(…)省略号表示
    用CSS设置Table的细边框的最好用的方法
    web app 自适应方案总结 弹性布局之rem
  • 原文地址:https://www.cnblogs.com/itdef/p/9740797.html
Copyright © 2020-2023  润新知