用C语言开发一个BT下载软件 (四) ------ 代码实现-1-种子文件解析模块
//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-种子文件解析模块相关推荐
- c语言bt下载程序,用C语言开发一个BT下载软件(一) ------ BitTorrent协议 -1
BitTorrent(简称BT)是一个文件分发协议,每个下载者在下载的同时不断地向其他下载者上传已下载的数据.它是属于一个应用层的协议. 基于BT协议的文件分发系统由以下几个实体构成: 一个web服务 ...
- 用C语言开发一个BT下载软件(一) ------ BitTorrent协议 -1
BitTorrent(简称BT)是一个文件分发协议,每个下载者在下载的同时不断地向其他下载者上传已下载的数据.它是属于一个应用层的协议. 基于BT协议的文件分发系统由以下几个实体构成: 一个web服务 ...
- 用C语言开发一个BT下载软件 (三) ------ 系统结构设计
整个系统各个模块功能如下图所示: 种子解析:负责解析种子文件,从中获取Tracker服务器的地址,待下载文件的文件名和长度,piece长度,各个piece的hash值. 连接Tracker:根据HTT ...
- 用C语言开发一个BT下载软件 (二) ------ 算法和策略
流水线作业 当客户端向peer发送数据请求时(即发送request消息),一次请求多个slice(即在一个数据包中发送多个request消息请求多个slice).peer发送完一个slice后接着发送 ...
- 鸿蒙系统开发实战-开发一个聊天技巧软件堪称聊天神器
鸿蒙开发实战-开发一个聊天助手APP 鸿蒙系统开发实战-开发一个聊天技巧软件堪称聊天神器.目前鸿蒙系统可真是过了一把自主研发的瘾,通过一个鸿蒙程序开发实战教程来演示如何开发一款聊天神器,视频教程放在了 ...
- linux 安装bt下载软件,centos6.3安装linux下的BT软件qbittorrent的方法
原创内容,转载请注明出处:https://www.myzhenai.com/thread-15430-1-1.html https://www.myzhenai.com.cn/post/942.htm ...
- 基于易语言开发的小说下载工具
基于易语言开发的小说下载工具 这次的编程,是为了交作业,嘻嘻 老规矩,上个图 ***让我女朋友露个脸,哈哈哈哈哈*** 起因:看到论坛里面,没什么用易语言下载小说的软件,是易语言不好用吗?还是Pyth ...
- 用Go语言开发一个编程语言
用Go语言开发一个编程语言 最近小弟业余时间闲来无事,所以就尝试一下自己开发一个轻量级的解释型语言. 出于学习的目的,我目前已经利用业余时间开发了一个多月,目前实现了变量声明,内置函数调用.自定义函数 ...
- 带你开发一个远程控制项目---->STM32+标准库+阿里云平台+传感器模块+远程显示-------之 MQTT连接阿里云平台
目录 第一篇: 第二篇: 项目清单 视频验证效果 Android Studio开发介绍 步1:此次需要下载本人开发的MQTT阿里云连接项目 步2:替换阿里云 设备三元信息 查看三元 替换 Androi ...
- 【从零开始制作 bt 下载器】一、了解 torrent 文件
[从零开始制作 bt 下载器]一.了解 torrent 文件 写作背景 读取 torrent 文件 认识 bencode 使用 Python 解析 torrent 文件 解密 torrent 文件 结 ...
最新文章
- S3C2440-中断体系架构
- STM32关于BOOT0和BOOT1设置,去掉Debug后完成硬件独立运行。
- 【编程6】贪吃蛇游戏(python+pygame)
- C. Anton and Making Potions 贪心 + 二分
- android点击弹出日期选择器,关于Android 点击按钮弹出日期选择器
- [渝粤教育] 中国地质大学 微积分(一) 复习题 (2)
- linux 64 32 编译支持,在64位linux上编译32位程序 for i386 intel
- html内容封装为一个对象_技术赋能还是内容为王,哪一个才是短视频创作的关键?...
- vsftp账号_Linux下vsftp的重新安装和配置虚拟用户
- 2021牛客寒假算法基础集训营5,签到题BF
- linux 进程和线程或线程和线程之间通过管道通信(pipe)
- 微信对网络影响的技术试验及分析(论文全文)
- Django入门教程
- mysql计算员工年终奖金_年终奖 扣税计算
- 计算机视觉c刊论文,摄影外文文献 摄影核心期刊参考文献哪里找
- 浅谈集合List,Set以及Map集合的特点及区别
- SQLite学习笔记(七)-- 数据插入、更新和删除(C++实现)
- 【安全攻防知识-3】学习平台搭建汇总
- topcoder srm 715 div1 -23
- c语言visit函数作用,[求助]二叉树遍历的程序里面的visit函数如何实现