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] = '\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)

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)

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 }
参考
《linux c编程实战》第十三章节btcorrent 及代码
欢迎转帖 请保持文本完整并注明出处
技术博客 http://www.cnblogs.com/itdef/
B站算法视频题解
https://space.bilibili.com/18508846
qq 151435887
gitee https://gitee.com/def/
欢迎c c++ 算法爱好者 windows驱动爱好者 服务器程序员沟通交流
如果觉得不错,欢迎点赞,你的鼓励就是我的动力


【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话