BT下载软件剖析

BT下载软件剖析

一、软件介绍

1.1 概要介绍

在传统的场景下,当我们想要下载一个文件时,通常是通过HTTP/FTP的方式从服务器上进行下载,这样的缺点是,服务器的带宽有限,当下载的人数过多,服务器的带宽被打满时,可能会导致下载很慢或无法下载。

而BT协议的出现就是为了解决这一问题,BT协议是一种P2P的传输协议,对于大文件、多人同时下载时的效率非常高。

它将文件进行分片,下载的时候一个分片一个分片地下载,同时将自己已经下载的文件分散给其他正在下载的用户,从而将原本服务器的压力分散给了终端用户。用户形成一个图结构,互相点对点地分发文件片段,下载的人越多,下载速度就越快。

本文主要剖析基于BT协议的文件分发系统的各个模块,并提出关于优化的思考。

1.2 系统结构
1.2.1 BT系统包括以下几个实体
image-20220702230224937
1.2.2 客户端下载一个共享文件的流程
image-20220703160307551
1.2.3 系统涉及到的技术
image-20220702163509196
1.2.4 peer消息类型

BT客户端和不同peer之间的通信是通过不同的消息类型来进行的,peer通过识别消息类型,能够识别不同peer的状态,做出不同响应,包括以下消息类型:

iShot2022-07-02 23.35.57
1.2.5 系统结构:
image-20220702163535615 image-20220702164637803

二、各模块剖析

2.1 种子解析模块

该模块主要的功能就是获取信息,从Web服务器获取种子文件,解析种子文件的内容,获取共享文件(即待下载文件)的文件名、文件大小,最主要的是获取tracker服务器的地址,同时还会判断要下载的是单文件还是多文件,计算每个piece的hash值,并放到缓存里。

函数功能
int read_metafile(char *metafile_name)该函数的作用是用来解析种子文件,并将种子文件的内容读入缓冲区
int find_keyword(char *keyword,long *position)从种子文件中查找某个关键字,即通过关键字从种子文件中查找信息。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g3mwfM31-1657467498244)(https://cdn.jsdelivr.net/gh/2714222609/pic-bed/img/image-20220702171033585.png)]
read_announce_list()这里是通过查找关键字来获取Tracker地址,并将获取的地址保存到全局变量announce_list_head指向的链表中。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mUbEO5sG-1657467498245)(https://cdn.jsdelivr.net/gh/2714222609/pic-bed/img/image-20220702171234210.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-71msupED-1657467498245)(https://cdn.jsdelivr.net/gh/2714222609/pic-bed/img/image-20220702171307756.png)]
int add_an_announce(char *url)peer的URL可能会发生重定向,该部分的功能就是将新的URL记录到Tracker列表中。
int is_multi_files()判断是下载多个文件还是单文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MKXCCahv-1657467498245)(https://cdn.jsdelivr.net/gh/2714222609/pic-bed/img/image-20220702171631131.png)]
int get_piece_length()获取piece的长度
get_pieces()获取每个piece的hash值,并保存到pieces所指向的缓冲区中
get_file_name()获取待下载的文件的文件名,如果下载的是多个文件,则获取的是目录名
int get_file_length()获取待下载文件的长度
get_files_length_path()对于多文件,获取各个文件的路径以及长度
int get_info_hash ()计算info_hash的值
int get_peer_id()生成一个惟一的peer id
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nHggZJZT-1657467498245)(https://cdn.jsdelivr.net/gh/2714222609/pic-bed/img/image-20220702172014807.png)]
void release_memory_in_parse_metafile()释放动态申请的内存
int parse_metafile(char *metafile)该函数为种子解析模块的入口,调用parse_metafile.c中定义的函数,完成解析种子文件

解析种子文件流程:

iShot2022-07-02 17.28.52
2.2 位图管理模块

BT协议采用位图指明当前哪些piece已经下载,哪些piece还没有下载,每个piece占一位,值为0表示该piece还未下载到,为1则表明已经下载到该piece。

本模块负责管理位图,客户端与peer建立了连接并进行握手之后,即发送位图给peer告知已下载到哪些piece,同时也接收对方的位图并将其保存在Peer结构体中。每下载到一个piece就更新自己的位图,并发送have消息给所有已建立连接的peer。每当接收到peer发来的have消息就更新该peer的位图。

函数功能
int create_bitfield()在该函数中,创建了待下载文件的位图,用pieces的长度除以20得到总的piece数。
image-20220702180218355
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BcfQMnod-1657467498246)(https://cdn.jsdelivr.net/gh/2714222609/pic-bed/img/image-20220702180238210.png)]
int get_bit_value(Bitmap *bitmap,int index)获取位图中的某一位,用来判断该位对应的piece是否已下载。
int set_bit_value(Bitmap *bitmap,int index,unsigned char value)设置位图中某一位的值,当piece下载完成后,将对应的位从0改为1
int all_zero(Bitmap *bitmap)将位图所有位清0
int all_set(Bitmap *bitmap)将位图所有位放置1
void release_memory_in_bitfield()释放本模块所申请的动态内存
int is_interested(Bitmap *dst,Bitmap *src)判断两个peer是否感兴趣,如果peer1拥有某个piece,而peer2没有,则peer2对peer1感兴趣,希望从peer1处下载它没有的piece。
int get_download_piece_num()获取当前已下载到的总piece数
2.3 出错处理模块

该模块主要定义了一些错误类型,在系统发生错误时能够合理处理,防止错误导致程序终止。

bterror.h
#ifndef BTERROR_H
#define BTERROR_H

#define FILE_FD_ERR						-1  	// 无效的文件描述符
#define FILE_READ_ERR					-2  	// 读文件失败
#define FILE_WRITE_ERR					-3  	// 写文件失败
#define INVALID_METAFILE_ERR			-4  	// 无效的种子文件
#define INVALID_SOCKET_ERR			-5  	// 无效的套接字
#define INVALID_TRACKER_URL_ERR		-6  	// 无效的Tracker URL
#define INVALID_TRACKER_REPLY_ERR	-7  	// 无效的Tracker回应
#define INVALID_HASH_ERR				-8  	// 无效的hash值
#define INVALID_MESSAGE_ERR			-9  	// 无效的消息
#define INVALID_PARAMETER_ERR		-10 	// 无效的函数参数
#define FAILED_ALLOCATE_MEM_ERR		-11 	// 申请动态内存失败
#define NO_BUFFER_ERR					-12	// 没有足够的缓冲区
#define READ_SOCKET_ERR				-13 	// 读套接字失败
#define WRITE_SOCKET_ERR				-14 	// 写套接字失败
#define RECEIVE_EXIT_SIGNAL_ERR		-15 	// 接收到退出程序的信号
2.4 运行日志模块

负责记录程序运行的日志,日志可以用来查询和分析程序行为。

log.h
#ifndef  LOG_H
#define  LOG_H
#include <stdarg.h>

// 用于记录程序的行为
void logcmd(char *fmt,...);

// 打开日志文件
int init_logfile(char *filename);

// 将程序运行日志记录到文件
int logfile(char *file,int line,char *msg);

#endif
2.5 信号处理模块

该模块用来处理SIGINT、SIGTERM等信号,不处理的话会导致程序终止,一些资源未来得及释放。因此为每个信号编写接口函数,收到信号后在函数中进行善后操作。

signal_hander.h
#ifndef SIGNAL_HANDER_H
#define SIGNAL_HANDER_H

// 做一些清理工作,如释放动态分配的内存
void do_clear_work();
// 处理一些信号
void process_signal(int signo);
// 设置信号处理函数
int set_signal_hander();

#endif
2.6 Peer管理模块

该模块用来管理peer,每个peer会被构造成为一个结构体,然后用链表的形式将每个结构体连接起来,该模块负责对链表结点进行创建,删除等操作。

peer结构体:

typedef struct  _Peer {
     int            socket;               	// 通过该socket与peer进行通信
     char           ip[16];               	// peer的ip地址
     unsigned short   port;                	// peer的端口号
     char           id[21];               	// peer的id
     
     int            state;                 	// 当前所处的状态
     int            am_choking;          	  // 是否将peer阻塞
     int            am_interested;         	// 是否对peer感兴趣
     int            peer_choking;          	// 是否被peer阻塞
     int            peer_interested;      	// 是否被peer感兴趣
     
     Bitmap         bitmap;               	// 存放peer的位图
     
     char          *in_buff;             	  // 存放从peer处获取的消息
     int            buff_len;             	// 缓存区in_buff的长度
     char          *out_msg;             	  // 存放将发送给peer的消息
     int            msg_len;              	// 缓冲区out_msg的长度
     char          *out_msg_copy;         	// out_msg的副本,发送时使用该缓冲区
     int            msg_copy_len;         	// 缓冲区out_msg_copy的长度
     int            msg_copy_index;       	// 下一次要发送的数据的偏移量
     
     Request_piece  *Request_piece_head;   	// 向peer请求数据的队列
     Request_piece  *Requested_piece_head; 	// 被peer请求数据的队列
     
     unsigned int    down_total;           	// 从该peer下载的总字节数
     unsigned int    up_total;              // 向该peer上传的总字节数
     
     time_t         start_timestamp;       	// 最近一次接收到peer消息的时间
     time_t         recet_timestamp;      	// 最近一次发送消息给peer的时间
     time_t         last_down_timestamp;   	// 最近下载数据的开始时间
     time_t         last_up_timestamp;     	// 最近上传数据的开始时间
     long long      down_count;       	    // 本计时周期从peer下载的数据的字节数
     long long      up_count;       	    	// 本计时周期向peer上传的数据的字节数
     float          down_rate;            	// 本计时周期从peer处下载数据的速度
     float          up_rate;           	  	// 本计时周期向peer处上传数据的速度
     
     struct _Peer   *next;									// next指针
} Peer;

peer一共定义了七种状态,状态信息反映了peer之间的连接是否正常,能否交互数据,是否阻塞等信息,以下是状态转换图:

image-20220702224745855
函数功能
initialize_peer(Peer *peer)初始化Peer结构体
Peer* add_peer_node()向peer链表添加一个结点
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pB9DI0gC-1657467498246)(https://cdn.jsdelivr.net/gh/2714222609/pic-bed/img/image-20220702225825126.png)]
int del_peer_node(Peer *peer)从peer链表中删除一个结点
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iEQTxht6-1657467498246)(https://cdn.jsdelivr.net/gh/2714222609/pic-bed/img/image-20220702225844638.png)]
int cancel_request_list(Peer *node)撤销当前请求队列
int cancel_requested_list(Peer *node)撤销当前被请求队列
void free_peer_node(Peer *node)释放一个peer结点的内存
void release_memory_in_peer()释放peer管理模块中动态申请的内存
2.7 消息处理模块

消息处理模块负责根据当前的状态生成并发送消息,接收以及处理消息。

该模块的函数都是在创建消息,接受并处理消息,就不一一列举,一下只列举几个重要的函数:

函数功能
int create_handshake_msg(char *info_hash,char *peer_id,Peer *peer)用来创建握手消息,并将握手消息存放在发送缓冲区中,可以看到握手消息格式固定,其中包括keyword、hash_info、peer_id等
image-20220702231455329
int is_complete_message(unsigned char *buff,unsigned int len,int *ok_len)判断缓冲区中是否存放了一条完整的消息,会检测握手、chocke、have、bitfield、piece等消息是否完整
int process_handshake_msg(Peer *peer,unsigned char *buff,int len)处理接收到的一条握手消息,若客户端刚完成初始化,会回复peer握手消息;若已经完成初始化,会将状态改为已握手,同时记录通信时间
int parse_response_uncomplete_msg(Peer *p,int ok_len)处理收到的消息,此处会只处理缓冲区中完整的信息,处理完之后将缓冲区中不完整的信息移动到缓冲的前面
int prepare_send_have_msg()主动发送have消息,告知所有的peer自己已拥有哪些piece
void discard_send_buffer(Peer *peer)即将与peer断开时,丢弃发送缓冲区中的消息
2.8 缓冲管理模块

网络IO中都设置有缓冲区(因为不能一边进行网络IO交互,一边将数据写入磁盘,CPU对磁盘的读取是比较慢的,所以先把数据缓存在内存里),将下载的数据先保存到缓冲区,缓冲到一定程度后再将数据写入硬盘。

peer请求数据时,先在缓冲区中寻找,若缓冲区中不存在所请求的数据,则读文件并把请求数据所在的piece预先读入到缓冲区中。

缓冲区结构定义:

// 每个Btcache结点维护一个长度为16KB的缓冲区,该缓冲区保存一个slice的数据
typedef struct _Btcache {
    unsigned char   *buff;        	// 指向缓冲区的指针
    int             index;       		// 数据所在的piece块的索引
    int             begin;       		// 数据在piece块中的起始位置
    int             length;      		// 数据的长度
    
    unsigned char   in_use;          // 该缓冲区是否在使用中
    unsigned char   read_write;   	// 是发送给peer的数据还是接收到的数据
                               			// 若数据是从硬盘读出,read_write值为0
                               			// 若数据将要写入硬盘,read_write值为1
    unsigned char   is_full;      	// 缓冲区是否满
    unsigned char   is_writed;    	// 缓冲区中的数据是否已经写入到硬盘中
    int            access_count; 		// 对该缓冲区的访问计数
    struct _Btcache *next;
} Btcache;

缓冲管理模块负责缓存的创建、将缓存写入磁盘、从磁盘读取数据到缓存等操作,以下是几个比较重要的函数及其功能:

函数功能
Btcache* initialize_btcache_node()创建Btcache结点,分配内存空间并对其成员的值进行初始化
int create-btcache()创建总大小为16K*1024bit即16MB的缓冲区,缓冲区中的每个Btcache节点也是通过链表来组织的:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MZLX59ZY-1657467498246)(https://cdn.jsdelivr.net/gh/2714222609/pic-bed/img/image-20220703000802198.png)]
int create_files()根据种子文件中的信息创建保存下载数据的文件。通过lseek和write两个函数来实现物理存储空间的分配
int write_btcache_node_to_harddisk(Btcache *node)将一个batche节点写入磁盘
int read_slice_from_harddisk(Btcache *node)从硬盘读取数据到缓存
int write_btcache_to_harddisk(Peer *peer)将一个缓冲区所有batche节点写入磁盘,并释放缓冲区:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WddpZjN6-1657467498246)(https://cdn.jsdelivr.net/gh/2714222609/pic-bed/img/image-20220703001205237.png)]
void clear_btcache()清空缓冲区
2.9 策略管理模块

该部分主要是计算各个peer的下载和上传速度,根据下载速度选择非阻塞peer,采用随机算法选择优化非阻塞peer,以及实现片断选择策略。

该模块就是用来控制peer和peer之间的交互,选择效率最高的”合作者“来进行合作,同时,peer给客户端提供数据,客户端也会给peer提供数据;若peer只会”拿来主义“,那么客户端会使其阻塞。

策略就是每10s计算一下各个peer从该客户端的下载量,除以时间算出下载速度,然后选下载速度最快的四个peer解除阻塞,同时还会维护一个30s的特殊peer,为了使该特殊peer下次能够将客户端解除阻塞,而不至于客户端没有地方下载数据。

iShot2022-07-03 00.43.11
函数功能
int is_in_unchoke_peers(Peer *node)判断一个peer是否已经存在于unchoke_peers
int get_last_index(Peer **array,int len)从unchoke_peers中获取下载速度最慢的peer的索引
int select_unchoke_peer()找出当前下载速度最快的4个peer,将其解除阻塞
int get_rand_numbers(int length)打乱要下载所有piece,主要为了实现片段选择算法
int select_optunchoke_peer()从peer队列中随机选择一个peer作为优化非阻塞peer
int compute_rate()计算最近一段时间(如10秒)每个peer的上传下载速度
int compute_total_rate()计算总的下载和上传速度
int is_seed(Peer *node)根据位图判断某peer是否为种子
2.10 连接Tracker模块

Tracker是指运行于远端服务器上的一个程序。这个程序的功能就是用来追踪到底有多少人在下载同一个文件,当用户连接到这个服务器后,就会获得一个下载者的清单,比如下载者的地址信息等;然后下载软件就会根据这个清单,来自动连上别人的电脑进行文件的下载。

该模块主要负责客户端向Tracker服务器请求获取peers的IP和Port,以及解析返回的数据。

函数功能
int create_request(…)构造发送到Tracker服务器的HTTP GET请求
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qV6YpkOD-1657467498247)(https://cdn.jsdelivr.net/gh/2714222609/pic-bed/img/image-20220703092550872.png)]
int get_response_type(char *buffer,int len,int *total_length)获取Tracker返回的消息的类型
int parse_tracker_response1(char *buffer,int ret,char *redirection,int len)解析第一种Tracker的回应消息,获取返回的peers,将每个peer的IP和Port保存到链表中
int parse_tracker_response2(char *buffer,int ret)解析第二种Tracker的回应消息
2.11 与peer交互数据模块

该模块用来管理peer之间的数据交互,包括监听各个socket,建立TCP连接等,关键函数为int download_upload_with_peers()

image-20220703095416081

三、对于优化的思考

3.1 从种子文件种查找某个关键字优化
3.1.1 优化点

在原文中,从种子文件种查找某个关键字是通过int find_keyword(char *keyword,long *position)这一函数实现的:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tKYdjiNT-1657467498247)(https://cdn.jsdelivr.net/gh/2714222609/pic-bed/img/image-20220703105722388.png)]

具体的查找方式是通过C语言库函数memcmp( )来从前往后扫描字符串并一一比对,找到则返回。

翻阅了memcmp( )的源码如下:

int __cdecl memcmp(const void *buf1, const void *buf2, size_t count) {
    if (!count) return (0);
    while (--count && *(char *) buf1 == *(char *) buf2) {
        buf1 = (char *) buf1 + 1;
        buf2 = (char *) buf2 + 1;
    }
    return (*((unsigned char *) buf1) - *((unsigned char *) buf2));
}

在外层通过for循环遍历metafile_content,内层使用while循环来比对每个字节,时间复杂度相当于O(n2),效率是否比较低?

3.1.2 优化方案

将查找方式从memcmp()替换为基于KMP算法的字符串匹配,KMP的时间复杂度为O(m+n),m是文本的长度,n是待匹配字符串的长度,可以大大提高效率。实现方法大致如下:

int strStr(char* haystack, char* needle) {
    int n = strlen(haystack), m = strlen(needle);
    if (m == 0) {
        return 0;
    }
    int pi[m];
    pi[0] = 0;
    for (int i = 1, j = 0; i < m; i++) {
        while (j > 0 && needle[i] != needle[j]) {
            j = pi[j - 1];
        }
        if (needle[i] == needle[j]) {
            j++;
        }
        pi[i] = j;
    }
    for (int i = 0, j = 0; i < n; i++) {
        while (j > 0 && haystack[i] != needle[j]) {
            j = pi[j - 1];
        }
        if (haystack[i] == needle[j]) {
            j++;
        }
        if (j == m) {
            return i - m + 1;
        }
    }
    return -1;
}
3.2 Peer管理模块优化
3.2.1 优化点

系统在管理各个Peer时使用的是链表结构,将每个Peer构造为结构体,使用next指针连接起来形成链表,以下是原文:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hOVqvikj-1657467498247)(/Users/yue/Library/Application Support/typora-user-images/image-20220703111528078.png)]

而在链表中进行添加、删除操作时需要从头到尾遍历,找到对应的位置,时间复杂度O(n),当peer数量较多,遍历可能使得效率较低,以下是添加和删除一个结点的代码:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vizOFNqw-1657467498247)(https://cdn.jsdelivr.net/gh/2714222609/pic-bed/img/image-20220703111840538.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BGVncQHY-1657467498247)(https://cdn.jsdelivr.net/gh/2714222609/pic-bed/img/image-20220703111858881.png)]

3.2.2 优化方案

将链表管理方式优化为Hash表管理,以peer_id作为key,peer struct作为value,这样在进行添加、删除、查找操作时,时间复杂度为O(1),如要维持链表结构,可以在每个hash node中添加两个前后指针,形成一个有序的hash表,类似于Java uitl包中的LinkedHashMap,这样既可以以链表的形式从头到尾遍历每个peer,又能够在O(1)时间复杂度下进行增删改查,其结构大致如下:

WX20190606-145838@2x.png
3.3 Peer通信协议优化
3.3.1 优化点

原文中提到,peer之间的通信协议(peer wire protocal)是一个基于TCP协议实现的应用层协议。

而我们知道,TCP是一种面向连接的、可靠的、字节流协议,通过三次握手来建立连接、使用序列号、检验和、应答机制、滑动窗口、超时重传等一系列机制来保证传输数据的可靠性。这一系列机制使得TCP相较于UDP而言,效率更低。

3.3.2 优化方案

将TCP协议改成UDP协议,加快传输效率,UDP不属于连接协议,具有资源消耗少,处理速度快的优点。

但这样做的话,需要考虑如何保证数据传输的可靠性,UDP在传输层是无法保证数据的可靠性的,所以需要放到应用层来保证,在应用层实现保证传输可靠性的机制,使得传输效率尽可能高。

最简单的方式是在应用层模仿传输层TCP的可靠性传输。不考虑拥塞处理,可靠UDP的简单设计。

  • 1、添加seq/ack机制,确保数据发送到对端
  • 2、添加发送和接收缓冲区,主要是用户超时重传。
  • 3、添加超时重传机制。

发送端发送数据时,生成一个随机seq=x,然后每一片按照数据大小分配seq。数据到达接收端后接收端放入缓存,并发送一个ack=x的包,表示对方已经收到了数据。发送端收到了ack包后,删除缓冲区对应的数据。时间到后,定时任务检查是否需要重传数据。

3.4 缓冲区优化
3.4.1 优化点

原文中提到,缓冲管理模块是维护一个大小为16MB的缓冲区,下载时,先把下载的数据保存到缓冲区,达到一定程度后再进行刷盘;同时,其他peer来下载数据时,如果缓冲区没有,当前客户端会先把数据加载到客户端,在传输给peer。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ls0cz4ao-1657467498247)(https://cdn.jsdelivr.net/gh/2714222609/pic-bed/img/image-20220703150614555.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-90I60E2q-1657467498247)(/Users/yue/Library/Application Support/typora-user-images/image-20220703150626601.png)]

缓冲区被分为1024个节点,以16个节点为一个整体进行操作。

这样做可以有效起到缓存的作用,提高其它peer从当前客户端下载的速度。

但是原文中好像没有提到有缓存淘汰的机制,考虑以下场景:假设某时间段有四个peer同时从当前客户端下载数据,peer 1想要下载piece1、piece2,客户端从磁盘中将piece1、piece2加载到缓存,peer 1下载完毕。这时缓存满了,因为没有淘汰机制,所以将会调用

int release_read_btcache_node(int base_count)函数来释放缓存,该函数会随机选择piece进行释放。如果piece1、piece2被释放,当peer2想要获取数据时,又得重新从磁盘中将数据加载到内存。

3.4.2 优化方案

对缓存增加淘汰机制,可采用LRU、LFU等淘汰算法,尽可能的增大缓存复用率。

3.5 Tracker服务器优化
3.5.1 优化点

两个BT用户之间建立初始连接时是靠“tracker服务器”上面的“tracker URL”进行的。Tracker是指运行于远端服务器上的一个程序。这个程序的功能就是用来追踪到底有多少人在下载同一个文件,当用户连接到这个服务器后,就会获得一个下载者的清单,比如下载者的地址信息等;然后下载软件就会根据这个清单,来自动连上别人的电脑进行文件的下载。可以说Tracker服务器是BT下载的核心所在,如果没有此服务,BT下载软件就迷失了方向;

Tracker服务存储着每个peer的IP和端口号,如果Tracker服务器挂掉了,客户端就没有办法获取其他发送者信息。同时,如果有多个tracker服务器,而每个tracker服务器上的数据不互通,客户端可能需要查询多个tracker服务器,才能找到拥有相应资源的服务器。

3.5.2 优化方案

tracker服务器采用分布式存储,保证服务器的高可用性,同时,尽可能使得各个tracker服务器所拥有的资源互通,使得客户端能够快速获取到想要的peer信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值