我是靠谱客的博主 潇洒帆布鞋,最近开发中收集的这篇文章主要介绍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("%sn",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:%dn",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("n");
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("n");
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 fiailedn");
 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 fiailedn");
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 failedn", __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:%sn",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:%dn",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:%dn", 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 okn");
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:%lldn", 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)
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  及代码

转载于:https://www.cnblogs.com/itdef/p/9740797.html

最后

以上就是潇洒帆布鞋为你收集整理的bittorrent 学习(一) 种子文件分析与bitmap位图的全部内容,希望文章能够帮你解决bittorrent 学习(一) 种子文件分析与bitmap位图所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(50)

评论列表共有 0 条评论

立即
投稿
返回
顶部