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; }
相关文章推荐
- Linux BT下载(7)-种子解析模块设计与实现1
- BT下载软件开发笔记——种子解析模块的设计与实现
- 解析大型.NET ERP系统 权限模块设计与实现
- 用C语言开发一个BT下载软件 (四) ------ 代码实现-1-种子文件解析模块
- 实现通用化DLL调用模块的设计解析
- Linux BT下载(14)-策略管理模块的设计和实现
- 常见设计模式的解析和实现(C++)之十三-FlyWeight模式
- 常见设计模式的解析和实现(C++)之十二-ChainOfResponsibility模式
- 常见设计模式的解析和实现(C++)之十六-Strategy模式
- 设计模式解析的C++实现
- 常见设计模式的解析和实现(C++)之四-Prototype模式
- 常见设计模式的解析和实现(C++)之十四-Command模式
- 常见设计模式的解析和实现(C++)之八-Composite模式
- 常见设计模式的解析和实现(C++)之二-Abstract Factory模式
- 常见设计模式的解析和实现(C++)之七-Bridge模式
- 常见设计模式的解析和实现(C++)之六-Adapt模式
- 常见设计模式的解析和实现(C++)之十一-TemplateMethod模式
- 常见设计模式的解析和实现(C++)之十八-Iterator模式
- 常见设计模式的解析和实现
- 常见设计模式的解析和实现(C++)之二十一-完结篇