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

种子解析模块设计与实现2

int add_an_announce(char *url)
/*
*功能:连接某种Tracker时会返回一个重定向的URL,需要连接URL才能获得Peer
*传入参数:新的URL
*传出参数:无
*返回值:
*	0	待添加的URL已经存在
*	1	添加成功
*/
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()
/*
*判断下载多个文件还是单文件,若含有关键字“5:files”则说明下载的是多文件
*传入参数:无
*传出参数:无
*返回值:
*	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);
#endif
	return 0;
}

int get_piece_length()
/*
*功能:获取piece的长度
*传入参数:无
*传出参数:无
返回值:
* 0 成功
*	-1 失败
*/

int get_piece_length()
{
	long i;

	if( find_keyword("12:piece length", &i) == 1)
	{
		i = i + strlen("12:piece length");  //跳过"12:piece length"
		i++; //跳过 'i'
		while(metafile_content[i] != 'e')
		{
			piece_length = piece_length + 10 + (metafile_content[i] - '0');
			i++;
		}
	}
	else
	{
		return -1;
	}

#ifdef DEBUG
	printf("piece length:%d\n", piece_length);
#endif
	return 0;
}
程序说明:以下是某种子文件的一部分:   12:piece lengthi262144e6:pieces16900:...       
从中可以看出,关键字“12:piece length”后跟一个B编码的整数(以i作为其实字符, 以e作为终结字符)。256K(262144),说明每个piece的长度都是256KB(最后一个piece除外)。然后是关键字“6:pieces”,其对应的值是一个B编码的字符串,存放各个piece的hash值,16900是字符串的长度,该字符串长度除以20即为piece数,因为每个piece的hash值为固定的20字符。

int get_pieces()
/*
*功能:获取每个piece的hash值,并保存到pieces所指向的缓冲区中
*传入参数:无
*传出参数:无
返回值:
* 0 成功
*	-1 失败
*/
int get_pieces()
{
	long i;

	if( find_keyword("6:pieces", &i) == 1)
	{
		i = i + 8; 	//跳过"6:pieces"
		while(metafile_content[i] != ':')
		{
			pieces_length = piece_length * 10 + (metafile_content[i] - '0');
			i++;
		}
		i++;		//跳过':'
		pieces = (char *)malloc(pieces_length + 1);
	}
	else
	{
		return -1;
	}
#ifdef DEBUG
	printf("get_pieces ok\n");
#endif
	return 0;
}

int get_file_name()
/*
*功能:获取待下载的文件名,如果待下载的是目录则获取的是目录名
*传入参数:无
*传出参数:无
返回值:
* 0 成功
*	-1 失败
*/
int get_file_name()
{
    long i;
    int count = 0;

    if( find_keyword("4:name", &i) == 1)
    {
        i = i + 6;  //跳过"4:name"
        while(metafile_content[i] != ':')
        {
            count = count * 10 + (metafile_content[i] - '0');
            i++;
        }
        i++;    //跳过':'
        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);
#endif // DEBUG
    return 0;
}
程序说明:以下是一个完整的较简单的种子文件:
d8:announce32:http://tk.greedland.net/announce13:announce-list1132:http://tk.greedland.net/announcee113:http;//tk2.greedland.net/announceee13:creation
datei1187968874e4:infod6:lengthi119861
306e4:name31:[ymer][naruto][246][jp_cn].rmvb10:name.utf-831:[ymer][naruto][246][jp_cn].rmv
	b12:piece lengthi262144e6:pieces9160:...ee
关键字“13:creaton date”及其对应的值“i1187968874e”,它指明了创建种子文件的时间。此时间是指1970年以来的秒数。关键字“4:info”对应的值是一个字典,因为该关键字之后的第一个字符是B编码中字典的起始‘d’,与该起始符是文件末尾的倒数第二个‘e’。计算info_hash时,就是以关键字“4:info”对应的值作为输入,计算hash值,将得到的值作为info_hash。文件末尾最后一个‘e’与文件开头的‘d’对应,故一个种子文件本身是一个字典。
关键字“6:length”对应的值是待下载文件的长度,以字节为单位,可以大致地确定待下载文件的长度为119MB。
关键字“10:name.utf-8”对应的值也是待下载文件的文件名,用utf-8表示。

int get_file_length()
/*
*功能:获取待下载的长度
*传入参数:无
*传出参数:无
返回值:
* 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;  //跳过"6:length"
            i++;    //跳过'i'
            while(metafile_content[i] != 'e')
            {
                file_length = file_length * 10 + (metafile_content[i] - '0');
                i++;
            }
        }
    }

#ifdef DEBUG
    //printf("file_length:%1ld\n", file_length);
#endif // DEBUG
    return 0;
}

int get_files_length_path()
/*
*功能:对于多文件,获取各个文件的路径以及长度
*传入参数:无
*传出参数:无
*
*/
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++)
    {
        //获取文件length并存储在链表中
        if(memcmp(&metafile_content[i], "6:length", 8) == 0)
        {
            i = i + 8; //跳过 "6:length"
            i++;        //跳过'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;
            }
        }
        //获取文件payh并保存存储链表中
        if(memcpy(&metafile_content[i], "4:path", 6) == 0)
        {
            i = i + 6;   // 跳过"4:path"
            i++;    //跳过'l'
            count = 0;
            while(metafile_content[i] != ':')
            {
                count = count * 10 + (metafile_content[i] - '0');
                i++;
            }
            i++;    //跳过':'
            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;
	// }
#endif
    return 0;
}
程序说明:请参考下图理解:
Linux BT下载(8)-种子解析模块设计与实现2_第1张图片
关键字“5:files”表示种子是多文件种子,它对应的值是一个列表,而列表的每个元素都是字典,每个字典代表一个待下载的文件。之后,有一个关键字“6:length”及其值“i127025815e”,然后是关键字‘4:path’,其值为一列表“134:[BBsee出品][军情观察室 08.22].rmvbe”,最后‘e’和‘d’相对应。

int get_info_hash()
/*
*功能:计算info_hash的值
*传入参数:无
*传出参数:无
*返回值
    0 成功
    -1 失败
*/
int get_info_hash()
{
    int push_pop = 0;
    long i, begin, end;

    if(metafile_content == NULL)
    {
        return -1;
    }
    //begin的值表示的是关键字"4:info"对应值的其实下标
    if( find_keyword("4:info", &i) == 1)
    {
        begin = i + 6;
    }
    else
    {
        return -1;
    }
    i = i + 6;  //跳过"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++;    //跳过'i'
            if(i == filesize)
            {
                return -1;
            }
            while(metafile_content[i] != 'e')
            {
                if((i + 1) == filesize)
                {
                    return -1;
                }
                else
                {
                    i++;
                }
            }
            i++;
        }
        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++;        //跳过':'
            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 DEBUG
    printf("info_hash");
    for(i = 0; i < 20; i++)
    {
        printf("&.2x ", info_hash[i]);
    }
    printf("\n");
#endif // DEBUG
    return 0;
}
程序说明:在种子文件解析模块,由种子文件的内容计算info_hash的值是比较复杂的,但可以通过关键字“4:info”对应的值计算info_hash,该关键字对应的值是一个B编码的字典。以“SHA1”开头的函数在sha1.h和sha1.c可见。

int get_peer_id()
int get_peer_id()
{
    //设置产生随机数的种子
    srand(time(NULL));
    //使用rand函数生成一个随机数,并使用该随机数构造peer_id
    //peer_id前8位固定为-TT1000-
    // 生成随机数,并把其中12位赋给peer_id,peer_id前8位固定为-TT1000-
    sprintf(peer_id, "-TT1000-%12d", rand());

#ifdef DEBUG
    int i;
    printf("peer_id: ");
    for(i = 0; i < 20; i++)
    {
        printf("%c", peer_id[i]);
    }
    printf("\n");
#endif
    return 0;
}

void release_memory_in_parse_metafile()
/*
*功能:释放动态申请的内存
*传入参数:无
*传出参数:无
*返回值:无
*/
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)
/*
*功能:解析种子文件
*传入参数:metafile种子文件的路径及文件名
*传出参数:无
*返回值:0
*/
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的长度,一般为256KB
	ret = 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_id
	ret = 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;
}




你可能感兴趣的:(Linux,BT下载)