終於抽出時間來進行 BITTORRENT的學習了node
BT想必你們都很熟悉了,是一種文件分發協議。每一個下載者在下載的同時也在向其餘下載者分享文件。linux
相對於FTP HTTP協議,BT並非從某一個或者幾個指定的點進行文件下載,而是用戶之間進行交互,每一個用戶既是下載者也是上傳者.ios
BT並不會出現提供下載的服務點出現問題而沒法下載的現象。git
我嘗試從BT文件開始下載的流程來分析下咱們須要那些功能。編程
首先咱們從某網站下載BT種子文件,文件很小,記錄要下載的實際文件的一些信息。服務器
那麼咱們就須要從該BT種子文件解析出 文件的數量(好比視頻文件和文件字幕等多個文件),文件名稱,文件大小,還有最重要的鏈接何處網站獲取其餘用戶信息等等等等。網絡
這個就是種子解析模塊。ide
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 及代碼