//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); // 在种子文件中查找某个关键词
int read_announce_list();                       // 获取各个tracker服务器的地址
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关键词对应的值计算info_hash
int get_peer_id();            // 生成peer_id,每个peer都有一个20字节的peer_id// 释放parse_metafile.c中动态分配的内存
void release_memory_in_parse_metafile();
// 调用本文件中定义的函数,完成解析种子文件
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; // 保存种子文件的内容
long  filesize;                 // 种子文件的长度int       piece_length  = 0;    // 每个piece的长度,通常为256KB即262144字节
char      *pieces       = NULL; // 保存每个pieces的哈希值,每个哈希值为20字节
int       pieces_length = 0;    // pieces缓冲区的长度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服务器的URL// 读取种子文件
int read_metafile(char *metafile_name)
{long  i;// 以二进制、只读的方式打开文件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;}// 读取种子文件的内容到metafile_content缓冲区中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;
}// 在种子文件中查找某个关键词
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服务器的地址
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');   /*本段代码,是将作为文本的数字,转换成整数类型的数字。乘10,是进位,多一位的话,先前的数字自然要进位所以要乘10,目前为止最后的数字作为个位*/i++;                                          /*减0,意思是以字符‘0’作为基准,计算某个数和0之间的差,则该差的值,就是那个字符型数字对应的整数型数字了*/}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关键词就不用处理8:announce关键词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指向最后个结点}p->next = node; // node成为tracker列表的最后一个结点}}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;// 若参数指定的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关键词对应的值计算info_hash
/*
由关键字“4:info”对应的值来计算info_hash,该关键字对应的是一个B编码的字典,问题的关键在找到与'd'对应的‘e’
思路是:每当遇到字典和列表起始符'd',则将push_pop的值加1遇到整数的起始符‘i’则一直扫描直到找到与之对应的终结符'e'遇到一个0~9的数字说明接下来是字符串,跳过该字符串继续扫描遇到‘e’则将push_pop减1,减1后,push_pop值为0,说明已经找到了与‘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是关键字"4:info"对应值的起始下标} 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字节的peer_id
int get_peer_id()
{// 设置产生随机数的种子srand(time(NULL));// 生成随机数,并把其中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中动态分配的内存
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)
{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的长度,一般为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;
}

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

  1. c语言bt下载程序,用C语言开发一个BT下载软件(一) ------ BitTorrent协议 -1

    BitTorrent(简称BT)是一个文件分发协议,每个下载者在下载的同时不断地向其他下载者上传已下载的数据.它是属于一个应用层的协议. 基于BT协议的文件分发系统由以下几个实体构成: 一个web服务 ...

  2. 用C语言开发一个BT下载软件(一) ------ BitTorrent协议 -1

    BitTorrent(简称BT)是一个文件分发协议,每个下载者在下载的同时不断地向其他下载者上传已下载的数据.它是属于一个应用层的协议. 基于BT协议的文件分发系统由以下几个实体构成: 一个web服务 ...

  3. 用C语言开发一个BT下载软件 (三) ------ 系统结构设计

    整个系统各个模块功能如下图所示: 种子解析:负责解析种子文件,从中获取Tracker服务器的地址,待下载文件的文件名和长度,piece长度,各个piece的hash值. 连接Tracker:根据HTT ...

  4. 用C语言开发一个BT下载软件 (二) ------ 算法和策略

    流水线作业 当客户端向peer发送数据请求时(即发送request消息),一次请求多个slice(即在一个数据包中发送多个request消息请求多个slice).peer发送完一个slice后接着发送 ...

  5. 鸿蒙系统开发实战-开发一个聊天技巧软件堪称聊天神器

    鸿蒙开发实战-开发一个聊天助手APP 鸿蒙系统开发实战-开发一个聊天技巧软件堪称聊天神器.目前鸿蒙系统可真是过了一把自主研发的瘾,通过一个鸿蒙程序开发实战教程来演示如何开发一款聊天神器,视频教程放在了 ...

  6. linux 安装bt下载软件,centos6.3安装linux下的BT软件qbittorrent的方法

    原创内容,转载请注明出处:https://www.myzhenai.com/thread-15430-1-1.html https://www.myzhenai.com.cn/post/942.htm ...

  7. 基于易语言开发的小说下载工具

    基于易语言开发的小说下载工具 这次的编程,是为了交作业,嘻嘻 老规矩,上个图 ***让我女朋友露个脸,哈哈哈哈哈*** 起因:看到论坛里面,没什么用易语言下载小说的软件,是易语言不好用吗?还是Pyth ...

  8. 用Go语言开发一个编程语言

    用Go语言开发一个编程语言 最近小弟业余时间闲来无事,所以就尝试一下自己开发一个轻量级的解释型语言. 出于学习的目的,我目前已经利用业余时间开发了一个多月,目前实现了变量声明,内置函数调用.自定义函数 ...

  9. 带你开发一个远程控制项目---->STM32+标准库+阿里云平台+传感器模块+远程显示-------之 MQTT连接阿里云平台

    目录 第一篇: 第二篇: 项目清单 视频验证效果 Android Studio开发介绍 步1:此次需要下载本人开发的MQTT阿里云连接项目 步2:替换阿里云 设备三元信息 查看三元 替换 Androi ...

  10. 【从零开始制作 bt 下载器】一、了解 torrent 文件

    [从零开始制作 bt 下载器]一.了解 torrent 文件 写作背景 读取 torrent 文件 认识 bencode 使用 Python 解析 torrent 文件 解密 torrent 文件 结 ...

最新文章

  1. S3C2440-中断体系架构
  2. STM32关于BOOT0和BOOT1设置,去掉Debug后完成硬件独立运行。
  3. 【编程6】贪吃蛇游戏(python+pygame)
  4. C. Anton and Making Potions 贪心 + 二分
  5. android点击弹出日期选择器,关于Android 点击按钮弹出日期选择器
  6. [渝粤教育] 中国地质大学 微积分(一) 复习题 (2)
  7. linux 64 32 编译支持,在64位linux上编译32位程序 for i386 intel
  8. html内容封装为一个对象_技术赋能还是内容为王,哪一个才是短视频创作的关键?...
  9. vsftp账号_Linux下vsftp的重新安装和配置虚拟用户
  10. 2021牛客寒假算法基础集训营5,签到题BF
  11. linux 进程和线程或线程和线程之间通过管道通信(pipe)
  12. 微信对网络影响的技术试验及分析(论文全文)
  13. Django入门教程
  14. mysql计算员工年终奖金_年终奖 扣税计算
  15. 计算机视觉c刊论文,摄影外文文献 摄影核心期刊参考文献哪里找
  16. 浅谈集合List,Set以及Map集合的特点及区别
  17. SQLite学习笔记(七)-- 数据插入、更新和删除(C++实现)
  18. 【安全攻防知识-3】学习平台搭建汇总
  19. topcoder srm 715 div1 -23
  20. c语言visit函数作用,[求助]二叉树遍历的程序里面的visit函数如何实现

热门文章

  1. cad指示箭头快捷键命令_47个快捷键+50个CAD技巧助你玩转CAD
  2. Linux内核开发-入门篇
  3. 蓝牙之十一 AVRCP协议
  4. 一分钟更换惠普m132nw激光打印机硒鼓/粉盒
  5. Java使用itextpdf生成PDF文件并添加斜面水印并完成下载(图片导出pdf)
  6. html里的表情,HTML 表情符号
  7. 如何批量转换图片格式为jpg?
  8. vecm模型怎么写系数_vecm(向量误差修正模型vecm)
  9. MATLAB画函数图像
  10. MATLAB--基本绘图函数