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

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

2016-11-26 13:42 417 查看

种子解析模块设计与实现

解析文件主要在parse_metafile.h和parse_metafile.c中实现。
//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列表添加一个url

int get_piece_length();	//获取每个piece的长度,一般为256KB
int get_pieces();		//获得各个piece的hash值

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

void release_memory_in_parse_metafile();	//释放parse_metafile.c中动态分配的内存
int parse_metafile(char *metafile);	//调用本文件中定义的函数,完成解析种子文件

#endif


以下是parse_metafile.c文件的头部
//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 "shal.h"

char *metafile_content = NULL;	//保存种子的内容
long filesize;					//种子的长度

int piece_length = 0;		//每个piece的长度一般为256KB
char *pieces = NULL;		//保存每个pieces的hash值,每个为20B
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;	//用于保存所有trackr的服务器的URL


具体函数如下:

int read_metafile(char *metafile_name),此函数的说明在代码中不再另写。
/*
* int read_metafile(char *metafile_name)
*解析种子文件
*metafile_name是种子文件名
*处理成功返回0,否则返回-1
*将种子文件的内容读入到全局变量metafile_content所指的缓存区中

*说明 编译器预定义的宏__FILE__和__LINE__在程序中可以直接使用。
*__FILE__代表该宏所在源文件的文件名,在源文件parse_metafile.c中
*该宏的值等于"parse_metafile.c",宏__LINE__的值为__LINE__所在的行
*号
*
*
*/
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;
}

//获得种子文件的长度,fieszie为全局变量,在parse_metafile_name
fseek(fp , 0, SEEK_END);
filesize = ftell(fp);
if(fileszie == -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 DEBUG
printf("metafile size is: &ld\n", filesize);
#endif
return 0;
}


int find_keyword(char *keyword,long *position)
/*
int find_keyword(char *keyword, long *position)
功能:从种子文件中查找某个关键字
参数:keyword为要查找的关键字,position用于返回关键字第一个字符所在的下标.
返回:成功找到关键字返回1,未找到返回0,执行失败返回-1
*/
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;
}


函数说明:此函数用于查找某个关键字(B编码的关键字)。例如:“8:announce”和“13:announce-list”之后是Tracker服务器的地址(URL),找到该关键字后,可以获取Tracker的地址。

int read_announce_list()
/*
功能:获得Tracker地址,并将获得的地址保存到全局变量announce_list_head指向的List中
*/
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)
{//无"13:announce-list"关键字
if( find_keyword("8:announce", &i) == 1)
{
//跳过"8:announce"
i = i + strlen("8:announce");	//i未加前是字符串的起始位置

//检测字符串后边的字符是否是阿拉伯数字
//获取URL的长度
while(isdigit(metafile_content[i]))
{
len = len * 10 + (metafile_content[i] - '0');
i++;
}
i++; //跳过':'

//申请堆空间保存Tracker的URL
node = (Announce_list *)malloc(sizeof(Announce_list));
strcpy(node->announce, &metafile_content[i], len);
node->announce[len] = '\0';
node->next = NULL;
announce_list_head = node;
}
}
else
{/*如果有13:announce-list关键字不用处理8:announce关键词
**使用备用的URL(备用URL中包含关键字"8:announce"包含的URL)
**关键字"13:announce-list"之后的第一个字符为列表的起始字符'l'
**该列表中含有两个元素,这两个元素的类型也都是列表
**
*/
i = i + strlen("13:announce-list");
i++;	//跳过'1'
while(metafile_content[i] != 'e')
{
i++;   //跳过'l'
/*
*检查输入的参数是否为阿拉伯数字
*获取URL的长度
*/

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));
strcpy(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++;	//跳过'e'
if(i >= filesize)
return -1;
}	//while循环结束
}
#ifdef DEBUG
p = announce_list_head;
while(p != NULL)
{
printf("%s\n", p->announce);
p = p->next;
}
#endif
return 0;
}


某种子文件的开头部分如下,可以帮助理解我的注释:

d8:announce32:http://tk.greedland.net/announce13:announce-listl132:http://tk.greedland.net/announcee113:http://th2.greedland.net/announceee...

第一个字符'd'是B编码中字典的起始符,接着是关键字“8:announce”,该关键字是长度为8的字符串,其对应的值为长度为32的字符串"32:http://tk.greedland,net/announce"是一个Tracker服务器地址(URL),接着是关键字"13:annunce-list",该关键字对应的是一个列表,应为关键字"13:announce-list"之后是第一个字符的起始字符"l",该列表中还有这两个元素的类型也都是列表。
如果有关键字"13:announce-list"哪吗就不用处理关键字"8:announce"的原因在于,前者对应的值必定包含对应的后者的值。

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