zoukankan      html  css  js  c++  java
  • 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驱动爱好者 服务器程序员沟通交流
    如果觉得不错,欢迎点赞,你的鼓励就是我的动力
    阿里打赏 微信打赏
  • 相关阅读:
    单例模式
    Curator Zookeeper分布式锁
    LruCache算法原理及实现
    lombok 简化java代码注解
    Oracle客户端工具出现“Cannot access NLS data files or invalid environment specified”错误的解决办法
    解决mysql Table ‘xxx’ is marked as crashed and should be repaired的问题。
    Redis 3.0 Cluster集群配置
    分布式锁的三种实现方式
    maven发布项目到私服-snapshot快照库和release发布库的区别和作用及maven常用命令
    How to Use Convolutional Neural Networks for Time Series Classification
  • 原文地址:https://www.cnblogs.com/itdef/p/9740797.html
Copyright © 2011-2022 走看看