日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

用C语言开发一个BT下载软件 (四) ------ 代码实现-1-种子文件解析模块

發(fā)布時間:2023/12/14 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 用C语言开发一个BT下载软件 (四) ------ 代码实现-1-种子文件解析模块 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
//parse_metafile.h#ifndef PARSE_METAFILE #define PARSE_METAFILE// 保存從種子文件中獲取的tracker的URL typedef struct _Announce_list {char announce[128];struct _Announce_list *next; } Announce_list;// 保存各個待下載文件的路徑和長度 typedef struct _Files {char path[256];long length;struct _Files *next; } Files; int read_metafile(char *metafile_name); // 讀取種子文件 int find_keyword(char *keyword,long *position); // 在種子文件中查找某個關(guān)鍵詞 int read_announce_list(); // 獲取各個tracker服務(wù)器的地址 int add_an_announce(char* url); // 向tracker列表添加一個URLint get_piece_length(); // 獲取每個piece的長度,一般為256KB int get_pieces(); // 讀取各個piece的哈希值int is_multi_files(); // 判斷下載的是單個文件還是多個文件 int get_file_name(); // 獲取文件名,對于多文件,獲取的是目錄名 int get_file_length(); // 獲取待下載文件的總長度 int get_files_length_path(); // 獲取文件的路徑和長度,對多文件種子有效int get_info_hash(); // 由info關(guān)鍵詞對應(yīng)的值計算info_hash int get_peer_id(); // 生成peer_id,每個peer都有一個20字節(jié)的peer_id// 釋放parse_metafile.c中動態(tài)分配的內(nèi)存 void release_memory_in_parse_metafile(); // 調(diào)用本文件中定義的函數(shù),完成解析種子文件 int parse_metafile(char *metafile);#endif


//parse_metafile.c#include <stdio.h> #include <ctype.h> #include <malloc.h> #include <stdlib.h> #include <string.h> #include <time.h> #include "parse_metafile.h" #include "sha1.h"char *metafile_content = NULL; // 保存種子文件的內(nèi)容 long filesize; // 種子文件的長度int piece_length = 0; // 每個piece的長度,通常為256KB即262144字節(jié) char *pieces = NULL; // 保存每個pieces的哈希值,每個哈希值為20字節(jié) int pieces_length = 0; // pieces緩沖區(qū)的長度int multi_file = 0; // 指明是單文件還是多文件 char *file_name = NULL; // 對于單文件,存放文件名;對于多文件,存放目錄名 long long file_length = 0; // 存放待下載文件的總長度 Files *files_head = NULL; // 只對多文件種子有效,存放各個文件的路徑和長度unsigned char info_hash[20]; // 保存info_hash的值,連接tracker和peer時使用 unsigned char peer_id[20]; // 保存peer_id的值,連接peer時使用Announce_list *announce_list_head = NULL; // 用于保存所有tracker服務(wù)器的URL// 讀取種子文件 int read_metafile(char *metafile_name) {long i;// 以二進(jìn)制、只讀的方式打開文件FILE *fp = fopen(metafile_name,"rb");if(fp == NULL) {printf("%s:%d can not open file\n",__FILE__,__LINE__);return -1;}// 獲取種子文件的長度fseek(fp,0,SEEK_END);filesize = ftell(fp);if(filesize == -1) {printf("%s:%d fseek failed\n",__FILE__,__LINE__);return -1;}metafile_content = (char *)malloc(filesize+1);if(metafile_content == NULL) {printf("%s:%d malloc failed\n",__FILE__,__LINE__);return -1;}// 讀取種子文件的內(nèi)容到metafile_content緩沖區(qū)中fseek(fp,0,SEEK_SET);for(i = 0; i < filesize; i++)metafile_content[i] = fgetc(fp);metafile_content[i] = '\0';fclose(fp); #ifdef DEBUGprintf("metafile size is: %ld\n",filesize); #endif return 0; }// 在種子文件中查找某個關(guān)鍵詞 int find_keyword(char *keyword,long *position) {long i;*position = -1;if(keyword == NULL) return 0;for(i = 0; i < filesize-strlen(keyword); i++) {if( memcmp(&metafile_content[i], keyword, strlen(keyword)) == 0 ) {*position = i;return 1;}}return 0; }// 獲取各個tracker服務(wù)器的地址 int read_announce_list() {Announce_list *node = NULL;Announce_list *p = NULL;int len = 0;long i;if( find_keyword("13:announce-list",&i) == 0 ) { /*只有一個announce,即只有一個Tracker的URL,無備用URL*/if( find_keyword("8:announce",&i) == 1 ) {i = i + strlen("8:announce");while( isdigit(metafile_content[i]) ) {len = len * 10 + (metafile_content[i] - '0'); /*本段代碼,是將作為文本的數(shù)字,轉(zhuǎn)換成整數(shù)類型的數(shù)字。乘10,是進(jìn)位,多一位的話,先前的數(shù)字自然要進(jìn)位所以要乘10,目前為止最后的數(shù)字作為個位*/i++; /*減0,意思是以字符‘0’作為基準(zhǔn),計算某個數(shù)和0之間的差,則該差的值,就是那個字符型數(shù)字對應(yīng)的整數(shù)型數(shù)字了*/}i++; // 跳過 ':'node = (Announce_list *)malloc(sizeof(Announce_list));strncpy(node->announce,&metafile_content[i],len);node->announce[len] = '\0';node->next = NULL;announce_list_head = node;}} else { // 如果有13:announce-list關(guān)鍵詞就不用處理8:announce關(guān)鍵詞i = i + strlen("13:announce-list");i++; // skip 'l'while(metafile_content[i] != 'e') {i++; // skip 'l'while( isdigit(metafile_content[i]) ) {len = len * 10 + (metafile_content[i] - '0');i++;}if( metafile_content[i] == ':' ) i++;else return -1;// 只處理以http開頭的tracker地址,不處理以udp開頭的地址if( memcmp(&metafile_content[i],"http",4) == 0 ) {node = (Announce_list *)malloc(sizeof(Announce_list));strncpy(node->announce,&metafile_content[i],len);node->announce[len] = '\0';node->next = NULL;if(announce_list_head == NULL)announce_list_head = node;else {p = announce_list_head;while( p->next != NULL){p = p->next; // 使p指向最后個結(jié)點(diǎn)}p->next = node; // node成為tracker列表的最后一個結(jié)點(diǎn)}}i = i + len;len = 0;i++; // skip 'e'if(i >= filesize) return -1;} }#ifdef DEBUGp = announce_list_head;while(p != NULL) {printf("%s\n",p->announce);p = p->next;} #endif return 0; }// 連接某些tracker時會返回一個重定向URL,需要連接該URL才能獲取peer int add_an_announce(char *url) {Announce_list *p = announce_list_head, *q;// 若參數(shù)指定的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() {long i;if( find_keyword("5:files",&i) == 1 ) {multi_file = 1;return 1;}#ifdef DEBUG// printf("is_multi_files:%d\n",multi_file); #endifreturn 0; }// 獲取每個piece的長度,一般為256KB int get_piece_length() {long i;if( find_keyword("12:piece length",&i) == 1 ) {i = i + strlen("12:piece length"); // skip "12:piece length"i++; // skip 'i'while(metafile_content[i] != 'e') {piece_length = piece_length * 10 + (metafile_content[i] - '0');i++;}} else {return -1;}#ifdef DEBUGprintf("piece length:%d\n",piece_length); #endifreturn 0; }// 讀取各個piece的哈希值 int get_pieces() {long i;if( find_keyword("6:pieces", &i) == 1 ) {i = i + 8; // skip "6:pieces"while(metafile_content[i] != ':') {pieces_length = pieces_length * 10 + (metafile_content[i] - '0');i++;}i++; // skip ':'pieces = (char *)malloc(pieces_length+1);memcpy(pieces,&metafile_content[i],pieces_length);pieces[pieces_length] = '\0';} else {return -1;}#ifdef DEBUGprintf("get_pieces ok\n"); #endifreturn 0; }// 獲取文件名,對于多文件,獲取的是目錄名 int get_file_name() {long i;int count = 0;if( find_keyword("4:name", &i) == 1 ) {i = i + 6; // skip "4:name"while(metafile_content[i] != ':') {count = count * 10 + (metafile_content[i] - '0');i++;}i++; // skip ':' 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); #endifreturn 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; // skip "6:length"i++; // skip 'i' while(metafile_content[i] != 'e') {file_length = file_length * 10 + (metafile_content[i] - '0');i++;} }}#ifdef DEBUGprintf("file_length:%lld\n",file_length); #endifreturn 0; }// 獲取文件的路徑和長度,對多文件種子有效 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++) {if( memcmp(&metafile_content[i],"6:length",8) == 0 ){i = i + 8; // skip "6:length"i++; // skip '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;}}if( memcmp(&metafile_content[i],"4:path",6) == 0 ){i = i + 6; // skip "4:path"i++; // skip 'l'count = 0;while(metafile_content[i] != ':') {count = count * 10 + (metafile_content[i] - '0');i++;}i++; // skip ':'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;// } #endifreturn 0; }// 由info關(guān)鍵詞對應(yīng)的值計算info_hash /* 由關(guān)鍵字“4:info”對應(yīng)的值來計算info_hash,該關(guān)鍵字對應(yīng)的是一個B編碼的字典,問題的關(guān)鍵在找到與'd'對應(yīng)的‘e’ 思路是:每當(dāng)遇到字典和列表起始符'd',則將push_pop的值加1遇到整數(shù)的起始符‘i’則一直掃描直到找到與之對應(yīng)的終結(jié)符'e'遇到一個0~9的數(shù)字說明接下來是字符串,跳過該字符串繼續(xù)掃描遇到‘e’則將push_pop減1,減1后,push_pop值為0,說明已經(jīng)找到了與‘d’匹配的‘e’ */ int get_info_hash() {int push_pop = 0;long i, begin, end;if(metafile_content == NULL)return -1;if( find_keyword("4:info",&i) == 1 ) {begin = i+6; // begin是關(guān)鍵字"4:info"對應(yīng)值的起始下標(biāo)} else {return -1;}i = i + 6; // skip "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++; // skip iif(i == filesize) return -1;while(metafile_content[i] != 'e') {if((i+1) == filesize) return -1;else i++;}i++; // skip e} 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++; // skip :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 DEBUGprintf("info_hash:");for(i = 0; i < 20; i++) printf("%.2x ",info_hash[i]);printf("\n"); #endifreturn 0; }// 生成peer_id,每個peer都有一個20字節(jié)的peer_id int get_peer_id() {// 設(shè)置產(chǎn)生隨機(jī)數(shù)的種子srand(time(NULL));// 生成隨機(jī)數(shù),并把其中12位賦給peer_id,peer_id前8位固定為-TT1000-sprintf(peer_id,"-TT1000-%12d",rand());#ifdef DEBUGint i;printf("peer_id:");for(i = 0; i < 20; i++) printf("%c",peer_id[i]);printf("\n"); #endifreturn 0; }// 釋放parse_metafile.c中動態(tài)分配的內(nèi)存 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);} }// 調(diào)用本文件中定義的函數(shù),完成解析種子文件 int parse_metafile(char *metafile) {int ret;// 讀取種子文件ret = read_metafile(metafile);if(ret < 0) { printf("%s:%d wrong",__FILE__,__LINE__); return -1; }// 從種子文件中獲取tracker服務(wù)器的地址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的長度,一般為256KBret = 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_idret = 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; }

總結(jié)

以上是生活随笔為你收集整理的用C语言开发一个BT下载软件 (四) ------ 代码实现-1-种子文件解析模块的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。