您的位置:首页 > 运维架构 > Linux

Linux BT下载(8)-种子解析模块设计与实现2

2016-11-27 14:01 399 查看

种子解析模块设计与实现2

int add_an_announce(char *url)
/*
*功能:连接某种Tracker时会返回一个重定向的URL,需要连接URL才能获得Peer
*传入参数:新的URL
*传出参数:无
*返回值:
*	0	待添加的URL已经存在
*	1	添加成功
*/
int add_an_announce(char *url)
{
Announce_list *p = announce_list_head, *q;

//若参数指定的URL在Tracker列表中已存在,则无需添加
while(p != NULL)
{
if(strcmp(p->announce,url) == 0)
break;
p=p->next;
}
if(p != NULL)
return 0;
q = (Announce_list *)malloc(sizeof(Announce_list));
strcpy(q->announce, url);
q->next = NULL;

p = announce_list_head;
if(p == NULL)
{
announce_list_head = q;
return 1;
}
while(p->next != NULL)
{
p = p->next;
}
p->next = q;
return 1;
}


int is_multi_files()
/*
*判断下载多个文件还是单文件,若含有关键字“5:files”则说明下载的是多文件
*传入参数:无
*传出参数:无
*返回值:
*	1	表示是多文件
*/
int is_multi_files()
{
long i;
if( find_keyword("5:files", &i) == 1)
{
multi_file = 1;
return 1;
}
#ifdef DEBUG
printf("is_multi_files:&d\n", multi_file);
#endif
return 0;
}


int get_piece_length()
/*
*功能:获取piece的长度
*传入参数:无
*传出参数:无
返回值:
* 0 成功
*	-1 失败
*/

int get_piece_length()
{
long i;

if( find_keyword("12:piece length", &i) == 1)
{
i = i + strlen("12:piece length");  //跳过"12:piece length"
i++; //跳过 'i'
while(metafile_content[i] != 'e')
{
piece_length = piece_length + 10 + (metafile_content[i] - '0');
i++;
}
}
else
{
return -1;
}

#ifdef DEBUG
printf("piece length:%d\n", piece_length);
#endif
return 0;
}
程序说明:以下是某种子文件的一部分:   12:piece lengthi262144e6:pieces16900:...       
从中可以看出,关键字“12:piece length”后跟一个B编码的整数(以i作为其实字符, 以e作为终结字符)。256K(262144),说明每个piece的长度都是256KB(最后一个piece除外)。然后是关键字“6:pieces”,其对应的值是一个B编码的字符串,存放各个piece的hash值,16900是字符串的长度,该字符串长度除以20即为piece数,因为每个piece的hash值为固定的20字符。

int get_pieces()
/*
*功能:获取每个piece的hash值,并保存到pieces所指向的缓冲区中
*传入参数:无
*传出参数:无
返回值:
* 0 成功
*	-1 失败
*/
int get_pieces()
{
long i;

if( find_keyword("6:pieces", &i) == 1)
{
i = i + 8; 	//跳过"6:pieces"
while(metafile_content[i] != ':')
{
pieces_length = piece_length * 10 + (metafile_content[i] - '0');
i++;
}
i++;		//跳过':'
pieces = (char *)malloc(pieces_length + 1);
}
else
{
return -1;
}
#ifdef DEBUG
printf("get_pieces ok\n");
#endif
return 0;
}

int get_file_name()
/*
*功能:获取待下载的文件名,如果待下载的是目录则获取的是目录名
*传入参数:无
*传出参数:无
返回值:
* 0 成功
*	-1 失败
*/
int get_file_name()
{
long i;
int count = 0;

if( find_keyword("4:name", &i) == 1)
{
i = i + 6;  //跳过"4:name"
while(metafile_content[i] != ':')
{
count = count * 10 + (metafile_content[i] - '0');
i++;
}
i++;    //跳过':'
file_name = (char *)malloc(count+1);
memcpy(file_name, &metafile_content[i], count);
file_name[count] = '\0';
}
else
{
return -1;
}
#ifdef DEBUG
// 由于可能含有中文字符,因此可能打印出乱码
// printf("file_name:%s\n",file_name);
#endif // DEBUG
return 0;
}
程序说明:以下是一个完整的较简单的种子文件:
d8:announce32:http://tk.greedland.net/announce13:announce-list1132:http://tk.greedland.net/announcee113:http;//tk2.greedland.net/announceee13:creation
datei1187968874e4:infod6:lengthi119861
306e4:name31:[ymer][naruto][246][jp_cn].rmvb10:name.utf-831:[ymer][naruto][246][jp_cn].rmv
b12:piece lengthi262144e6:pieces9160:...ee

关键字“13:creaton date”及其对应的值“i1187968874e”,它指明了创建种子文件的时间。此时间是指1970年以来的秒数。关键字“4:info”对应的值是一个字典,因为该关键字之后的第一个字符是B编码中字典的起始‘d’,与该起始符是文件末尾的倒数第二个‘e’。计算info_hash时,就是以关键字“4:info”对应的值作为输入,计算hash值,将得到的值作为info_hash。文件末尾最后一个‘e’与文件开头的‘d’对应,故一个种子文件本身是一个字典。
关键字“6:length”对应的值是待下载文件的长度,以字节为单位,可以大致地确定待下载文件的长度为119MB。
关键字“10:name.utf-8”对应的值也是待下载文件的文件名,用utf-8表示。

int get_file_length()
/*
*功能:获取待下载的长度
*传入参数:无
*传出参数:无
返回值:
* 0 成功
*
*/
int get_file_length()
{
long i;

if(is_multi_files() == 1)
{
if(files_head == NULL)
{
get_files_length_path();
}
Files *p = files_head;
while(p != NULL)
{
file_length += p->length;
p = p->next;
}
}
else
{
if( find_keyword("6:length", &i) == 1)
{
i = i + 8;  //跳过"6:length"
i++;    //跳过'i'
while(metafile_content[i] != 'e')
{
file_length = file_length * 10 + (metafile_content[i] - '0');
i++;
}
}
}

#ifdef DEBUG
//printf("file_length:%1ld\n", file_length);
#endif // DEBUG
return 0;
}


int get_files_length_path()
/*
*功能:对于多文件,获取各个文件的路径以及长度
*传入参数:无
*传出参数:无
*
*/
int get_files_length_path()
{
long i;
int length;
int count;
Files *node = NULL;
Files *p = NULL;

if(is_multi_files() != 1)
{//是否为多文件
return 0;
}
for(i = 0; i < filesize -8; i++)
{
//获取文件length并存储在链表中
if(memcmp(&metafile_content[i], "6:length", 8) == 0)
{
i = i + 8; //跳过 "6:length"
i++;        //跳过'i'
length = 0;
while(metafile_content[i] != 'e')
{
length = length * 10 + (metafile_content[i] - '0');
i++;
}
node = (Files *)malloc(sizeof(Files));
node->length = length;
node->next = NULL;
if(files_head == NULL)
{
files_head = node;
}
else
{
p = files_head;
while(p->next != NULL)
{
p = p->next;
}
p->next = node;
}
}
//获取文件payh并保存存储链表中
if(memcpy(&metafile_content[i], "4:path", 6) == 0)
{
i = i + 6;   // 跳过"4:path"
i++;    //跳过'l'
count = 0;
while(metafile_content[i] != ':')
{
count = count * 10 + (metafile_content[i] - '0');
i++;
}
i++;    //跳过':'
p = files_head;
while(p->next != NULL)
{
p = p->next;
memcpy(p->path, &metafile_content[i], count);
*(p->path + count) = '\0';
}
}

}
#ifdef DEBUG
// 由于可能含有中文字符,因此可能打印出乱码
// p = files_head;
// while(p != NULL) {
//	 printf("%ld:%s\n",p->length,p->path);
//	 p = p->next;
// }
#endif
return 0;
}
程序说明:请参考下图理解:



关键字“5:files”表示种子是多文件种子,它对应的值是一个列表,而列表的每个元素都是字典,每个字典代表一个待下载的文件。之后,有一个关键字“6:length”及其值“i127025815e”,然后是关键字‘4:path’,其值为一列表“134:[BBsee出品][军情观察室 08.22].rmvbe”,最后‘e’和‘d’相对应。

int get_info_hash()
/*
*功能:计算info_hash的值
*传入参数:无
*传出参数:无
*返回值
0 成功
-1 失败
*/
int get_info_hash()
{
int push_pop = 0;
long i, begin, end;

if(metafile_content == NULL)
{
return -1;
}
//begin的值表示的是关键字"4:info"对应值的其实下标
if( find_keyword("4:info", &i) == 1)
{
begin = i + 6;
}
else
{
return -1;
}
i = i + 6;  //跳过"4:info"
for(; i < filesize;)
{
if(metafile_content[i] == 'd')
{
push_pop++;
i++;
}
else if(metafile_content[i] == 'l')
{
push_pop++;
i++;
}
else if(metafile_content[i] == 'i')
{
i++;    //跳过'i'
if(i == filesize)
{
return -1;
}
while(metafile_content[i] != 'e')
{
if((i + 1) == filesize)
{
return -1;
}
else
{
i++;
}
}
i++;
}
else if((metafile_content[i] >= '0') && (metafile_content[i] <= '9'))
{
int number = 0;
while((metafile_content[i] >= '0') && (metafile_content[i] <= '9'))
{
number = number * 10 + metafile_content[i] - '0';
i++;
}
i++;        //跳过':'
i = i + number;
}
else if(metafile_content[i] == 'e')
{
push_pop--;
if(push_pop == 0)
{
end = i;
break;
}
else
{
i++;
}
}
else
{
return -1;
}
}
if(i == filesize)
{
return -1;
}
SHA1_CTX context;
SHA1Init(&context);
SHA1Update(&context, &metafile_content[begin], end-begin+1);
SHA1Final(info_hash, &context);

#ifdef DEBUG
printf("info_hash");
for(i = 0; i < 20; i++)
{
printf("&.2x ", info_hash[i]);
}
printf("\n");
#endif // DEBUG
return 0;
}
程序说明:在种子文件解析模块,由种子文件的内容计算info_hash的值是比较复杂的,但可以通过关键字“4:info”对应的值计算info_hash,该关键字对应的值是一个B编码的字典。以“SHA1”开头的函数在sha1.h和sha1.c可见。

int get_peer_id()
int get_peer_id()
{
//设置产生随机数的种子
srand(time(NULL));
//使用rand函数生成一个随机数,并使用该随机数构造peer_id
//peer_id前8位固定为-TT1000-
// 生成随机数,并把其中12位赋给peer_id,peer_id前8位固定为-TT1000-
sprintf(peer_id, "-TT1000-%12d", rand());

#ifdef DEBUG
int i;
printf("peer_id: ");
for(i = 0; i < 20; i++)
{
printf("%c", peer_id[i]);
}
printf("\n");
#endif
return 0;
}


void release_memory_in_parse_metafile()
/*
*功能:释放动态申请的内存
*传入参数:无
*传出参数:无
*返回值:无
*/
void release_memory_in_parse_metafile()
{
Announce_list *p;
Files         *q;

if(metafile_content != NULL)  free(metafile_content);
if(file_name != NULL)         free(file_name);
if(pieces != NULL)            free(pieces);

while(announce_list_head != NULL) {
p = announce_list_head;
announce_list_head = announce_list_head->next;
free(p);
}

while(files_head != NULL) {
q = files_head;
files_head = files_head->next;
free(q);
}
}


int parse_metafile(char *metafile)
/*
*功能:解析种子文件
*传入参数:metafile种子文件的路径及文件名
*传出参数:无
*返回值:0
*/
int parse_metafile(char *metafile)
{
int ret;

// 读取种子文件
ret = read_metafile(metafile);
if(ret < 0) { printf("%s:%d wrong",__FILE__,__LINE__); return -1; }

// 从种子文件中获取tracker服务器的地址
ret = read_announce_list();
if(ret < 0) { printf("%s:%d wrong",__FILE__,__LINE__); return -1; }

// 判断是否为多文件
ret = is_multi_files();
if(ret < 0) { printf("%s:%d wrong",__FILE__,__LINE__); return -1; }

// 获取每个piece的长度,一般为256KB
ret = get_piece_length();
if(ret < 0) { printf("%s:%d wrong",__FILE__,__LINE__); return -1; }

// 读取各个piece的哈希值
ret = get_pieces();
if(ret < 0) { printf("%s:%d wrong",__FILE__,__LINE__); return -1; }

// 获取要下载的文件名,对于多文件的种子,获取的是目录名
ret = get_file_name();
if(ret < 0) { printf("%s:%d wrong",__FILE__,__LINE__); return -1; }

// 对于多文件的种子,获取各个待下载的文件路径和文件长度
ret = get_files_length_path();
if(ret < 0) { printf("%s:%d wrong",__FILE__,__LINE__); return -1; }

// 获取待下载的文件的总长度
ret = get_file_length();
if(ret < 0) { printf("%s:%d wrong",__FILE__,__LINE__); return -1; }

// 获得info_hash,生成peer_id
ret = get_info_hash();
if(ret < 0) { printf("%s:%d wrong",__FILE__,__LINE__); return -1; }
ret = get_peer_id();
if(ret < 0) { printf("%s:%d wrong",__FILE__,__LINE__); return -1; }

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: