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

本文探讨Linux环境下,如何设计与实现BT种子解析模块,深入解析种子文件的内部结构和解析过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

种子解析模块设计与实现

解析文件主要在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"的原因在于,前者对应的值 必定包含对应的后者的值。

...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值