SimpleTorrent
宏定义 | 函数
driver.c 文件参考

顶层驱动模块 更多...

#include "butil.h"
#include "util.h"
#include "peer.h"
#include "connect.h"
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <errno.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <sys/timerfd.h>
driver.c 的引用(Include)关系图:

宏定义

#define BUF_SIZE   4096
 报文缓冲区大小
 

函数

void send_handshake (int sfd, struct MetaInfo *mi)
 发送握手信息
 
void send_msg_to_tracker (struct MetaInfo *mi, struct Tracker *tracker, const char *event)
 
int select_piece (struct MetaInfo *mi, struct PeerMsg *msg)
 选择需要请求的分片 更多...
 
struct Peerselect_peer (struct MetaInfo *mi, struct PeerMsg *msg)
 选择可以发送请求的 peer 更多...
 
void send_request (struct MetaInfo *mi, struct Peer *peer, struct PeerMsg *msg)
 向 peer 发送分片请求,同时更新 peer 和对应子分片的大小 更多...
 
void handle_piece (struct MetaInfo *mi, struct Peer *peer, struct PeerMsg *msg)
 处理分片消息 更多...
 
void handle_msg (struct MetaInfo *mi, struct Peer *peer, struct PeerMsg *msg)
 处理 BT 消息 更多...
 
struct BNodehandle_tracker_response (int sfd)
 处理 tracker 响应的 HTTP 报文,将解析后的语法树返回给上层使用,在内部关闭套接字 更多...
 
void handle_peer_list (struct MetaInfo *mi, int efd, struct BNode *bcode)
 将 tracker 返回的 peers 异步 connect 并加入 epoll 队列 更多...
 
void handle_interval (struct Tracker *tracker, struct BNode *bcode, int efd)
 提取 interval 信息并设置定时 更多...
 
void handle_error (struct MetaInfo *mi, int error_fd)
 处理出错套接字 更多...
 
void handle_ready (struct MetaInfo *mi, int sfd)
 执行连接建立后的相关操作 更多...
 
int finish_handshake (struct MetaInfo *mi, int sfd)
 完成握手消息的处理 更多...
 
void bt_handler (struct MetaInfo *mi, int efd)
 处理所有网络报文 更多...
 
char * get_torrent_data_from_file (const char *torrent)
 将种子文件完全载入内存 更多...
 
int main (int argc, char *argv[])
 入口函数,完成种子文件的解析,全局信息的生成以及进入消息处理逻辑。
 

详细描述

顶层驱动模块

函数说明

void bt_handler ( struct MetaInfo mi,
int  efd 
)

处理所有网络报文

使用 epoll 侦听各个描述符的事件,根据事件属性和描述符的所属采取相应的操作。 主要涉及的描述符类型:

  1. 与 tracker 的连接套接字
  2. 与 peer 的连接套接字
  3. tracker 的回访定时器
  4. 本机的 keep-alive 定时器

目前只对 peer 的 bt 消息做异步接受,其他报文基本要求同步地完全接受。

最多每 5s 处理一次发送报文的逻辑。目前来看可能出现 write 写满导致的阻塞。

参数
mi种子文件元信息
efdepoll file descriptor
待办事项:
通过 accept 建立连接并监听连接套接字的 EPOLLIN 事件,加入 MetaInfo::wait_peers 队列。注意设置 WaitPeer::direction

这是这个函数的调用关系图:

int finish_handshake ( struct MetaInfo mi,
int  sfd 
)

完成握手消息的处理

待办事项:
要区分是对方回复的握手消息还是对方主动发来的握手消息
返回
0: 正常, -1: 连接断开

这是这个函数的调用关系图:

char* get_torrent_data_from_file ( const char *  torrent)

将种子文件完全载入内存

参数
torrent种子文件名
返回
种子文件数据 [动态缓冲区]

这是这个函数的调用关系图:

void handle_error ( struct MetaInfo mi,
int  error_fd 
)

处理出错套接字

程序所使用的描述符主要有两种:socket fd 和 timer fd.

这里的错误处理逻辑针对 socket fd. Timer fd 相对来说并不那么容易出错。 函数会首先判断套接字是 tracker 的还是 peer 的,以输出错误信息并更新对应的队列。

参数
mi全局信息
error_fd出错的套接字

这是这个函数的调用关系图:

void handle_interval ( struct Tracker tracker,
struct BNode bcode,
int  efd 
)

提取 interval 信息并设置定时

参数
tracker指向发送响应的 tracker
bcode指向解析后的 B 编码语法树
efdepoll file descriptor,用于加入 timer fd

这是这个函数的调用关系图:

void handle_msg ( struct MetaInfo mi,
struct Peer peer,
struct PeerMsg msg 
)

处理 BT 消息

参数
mi全局信息
peer指向发送消息的 peer
msgpeer 发送的消息
待办事项:
根据 bitfield 增加 piece 持有者数量
待办事项:
根据 have 增加 piece 持有者数量
待办事项:
处理 REQUEST 消息
待办事项:
处理 CANCEL 消息

这是这个函数的调用关系图:

void handle_peer_list ( struct MetaInfo mi,
int  efd,
struct BNode bcode 
)

将 tracker 返回的 peers 异步 connect 并加入 epoll 队列

参数
mi全局信息
efdepoll 描述符
bcodeB 编码数据

这是这个函数的调用关系图:

void handle_piece ( struct MetaInfo mi,
struct Peer peer,
struct PeerMsg msg 
)

处理分片消息

收到分片消息后,期望调用者处理字节序。 会将子分片写入到对应的分片文件中,如果一个子分片已经被写入过,则抛弃。 出于简单实现的考虑,子分片采取固定大小,使用位图管理完成进度, 最后一个分片不会在这里进行特殊处理,由发送过程保证最后一个分片长度的正确性。

在这里结算一个子分片的下载速度。

这是这个函数的调用关系图:

void handle_ready ( struct MetaInfo mi,
int  sfd 
)

执行连接建立后的相关操作

与 tracker 和 peer 通过 connect 方式建立连接后(相对的,还有通过 accept 与 peer 建立连接的情形), 按照协议要求,要主动发送消息(HTTP 请求,握手)。 本函数首先根据套接字确定套接字所属的对象,然后发送对应的消息。

参数
mi全局信息
sfd连接套接字

这是这个函数的调用关系图:

struct BNode* handle_tracker_response ( int  sfd)

处理 tracker 响应的 HTTP 报文,将解析后的语法树返回给上层使用,在内部关闭套接字

考虑到 tracker 的响应数据量不大,内部全部使用 recv + MSG_WAITALL 防止 read 读取不足。

参数
sfd与 tracker 的连接套接字
返回
解析后的语法树,非法时返回 NULL

这是这个函数的调用关系图:

struct Peer* select_peer ( struct MetaInfo mi,
struct PeerMsg msg 
)

选择可以发送请求的 peer

msg 不要转换字节序

这是这个函数的调用关系图:

int select_piece ( struct MetaInfo mi,
struct PeerMsg msg 
)

选择需要请求的分片

采取最简单的前面的先下载策略。 使用输出参数 msg 是因为这种 msg 一般都是栈上生成的,用完就丢。

参数
mi全局信息
msg要发送的请求
返回
0 - 成功,-1 - 失败
待办事项:
应用优化策略:最少优先

这是这个函数的调用关系图:

void send_msg_to_tracker ( struct MetaInfo mi,
struct Tracker tracker,
const char *  event 
)

向 tracker 发送 HTTP GET 请求. 可以通过 event 指定发送的具体时间, 其余信息完全通过 mi 填写.

这是这个函数的调用关系图:

void send_request ( struct MetaInfo mi,
struct Peer peer,
struct PeerMsg msg 
)

向 peer 发送分片请求,同时更新 peer 和对应子分片的大小

修改 peer 状态,表明它处于下载任务中,同时更新起始时刻,用于之后计算速度。

修改子分片状态,表明它正被下载,同时更新起始时刻,用于之后超时检测。

msg 报文的本机字节序会转换为网络字节序。

参数
mi全局信息
peer指向要发送请求的 peer 的指针
msg构造好的请求报文,本机字节序

这是这个函数的调用关系图: