这篇博文主要是介绍 BitTorrent 中关于对等端收发的不同消息类型的定制,以及消息在网络中的表示方式与主机在接收到消息之后如何解析。
说道消息,无非就是将结构体中的多种不同属性类型字段的变量数值,写入到一个字符串中,这便是消息在发送之前,
从程序到缓冲区,然后再从缓冲区中被打包成数据包,发送到对等端的缓冲区中。
而接收消息的一端,先从缓冲区中会将数据提取到程序缓存变量中(char*, string ...)然后按照双方协定好的协议格式,
来一截一截的抽取指定长度的字节(或是bit),然后再将其转换为不同属性类型字段的变量数值。
而对于消息的处理,可以有如下的几种方法:
1. 直接基于字符串数组的操作 char *, unsigned char * ,这种方法写起来比较麻烦,而且很容易出错
2. 基于一些现有的C++ 标准库中封装的对象,例如使用string , vector 可以节省一部分方法的编写,
但是空间利用并不好,在将操作细化到 bit 的时候,使用 string 对象类型显然变得没有太多的优势而言
3. 仍旧使用 char * , unsigned char * 作为底层存储数据对象,但是基于其操作的方法使用正则表达式实现,
这种方法,有很大的优势,因为正则一向是字符串操作的利器,不过我不擅长正则表达式。
4. 使用第三方库来实现,有 MongoDB 中的 BSON, 和 google 中的 protocol buf 或是当下比较流行的 hadoop
中的 thrift ,或是 hadoop 中的子项目 avro。
我在使用过 1,2,方法之后觉得这种编程方法并不高效,而且看起来也并不具有很好的可读性,所以决定中途放弃这两种方法,
转而使用第 4 个方法。考虑到熟悉程度和库本身的复杂程度,只考虑 BSON 和 protobuf 两种情况
不过在使用方法 4 之前,有几个问题需要思考一下,那便是:
二者之间的区别是什么?
在当前的 BT 项目中哪一个库比较适合?
BSON 是以二进制的方式来表示 JSON 类型,但却并不仅仅是换了一种表达方式,在换了数据表示格式的同时又在
BSON 对象中封装了一些独特的方法。
BSON 比较适用于 shcema-less 的数据传输的封装与解析,而protobuf 比较适用于 shcema 类型数据传输的封装与解析。
打个比方,就是 BSON 更适合传送数据流格式的数据,在数据库之间传输数据表的时候,比较常使用。
而 protobuf 比较适用于传送哈希散列表,键值对这种有固定格式的数据,适合传送字节流格式的数据。
BitTorrent 中需要传输的数据格式可以从 BitTorrent 协议中获知
-
enum state // 当前对等端处于的状态
-
{
-
INITIAL = 0 ,
-
HALF_SHAKED,
-
HAND_SHAKED,
-
SEND_BIT_FIELD,
-
RECV_BIT_FIELD,
-
EXCHANGING_DATA,
-
CLOSING
-
} ;
-
-
const long MSG_LEN = (1024*2 + 1024*16) ; // 所传输消息的最大长度,单位是字节
-
-
typedef struct _request_piece // 请求文件 piece 的消息
-
{
-
int index ;
-
int begin ;
-
int length ;
-
} request_piece_t ;
-
-
typedef struct _peer_node // 一个对等端上面所包含的所有信息
-
{
-
int socket ; // ---> socket 连接套接字描述符
-
char ip [16] ; //-------> 当前端点的 IP 地址
-
unsigned short port ; //----> 当前端点的提供服务的端口号
-
char id[21] ; //----> 不同端点用于唯一标识自己的 id 号码
-
-
int state ; //-----> 当前下载资源端点所处的状态,由上面的枚举变量 enum state 定义
-
-
int am_chocking ; //-------> 标识当前端点是否处于“阻塞状态” 也就是不为其他端点提供下载服务的状态
-
int am_interested ; // 标识当前端点是否对其他端点“感兴趣” 也就是其他端点上有着当前端点没有的资源片段(pieces)
-
int peer_chocking ; //----------> 对等端是否阻塞,如果对等端阻塞,当前端点将不能从对等端上下载导数据
-
int peer_interested ; //--对等端是否对当前端点感兴趣,如果感兴趣,说明当前端点上有着对等端上没有的资源片段(pieces)
-
-
Bitmap *pBitmap ;
-
// 指向一个位图对象,该位图对象整体用于表示一个完整的资源文件,位图中的每一位,唯一索引文件中的一个资源片段(piece)
-
//位图中的每一位 若为 0 表示端点并不具有该文件片段的资源, 若为 1 说明端点具有该文件的资源,所以其中包含了一个 0,1 组成的字符串序列
-
-
std::string buff_in ; // 存储了当前端点接收来自其他端点的信息,作为接收消息缓冲区使用
-
-
std::string msg_out ; // 作为发送消息缓冲区使用,存储当前端点要发送到其他端点的消息
-
-
std::string msg_buff_out ; // 由于 socket 中的 send 函数一次发送的字符串长度有限,
-
//所以这个变量做 msg_out 的缓冲区,每次从 msg_oug 中取出固定长度的数据,通过 send 方法发送
-
-
-
// download reqeustes received from others
-
std::vector<request_piece_t> recv_download_request_queue ; // 消息请求队列,其中存放了接收来自其余对等端点的请求下载消息
-
// uploaded requests send to others
-
std::vector<request_piece_t> send_upload_request_queue ; //消息息请求队列,用来存放,当前端点发向对等端点发送的请求下载消息
-
-
unsigned int down_total ; // 这些是一些统计信息,用于综合算法的调用
-
unsigned int up_total ;
-
-
time_t start_timestamp ;
-
time_t recet_timestamp ;
-
-
time_t last_down_timestamp ;
-
time_t last_up_timestamp ;
-
-
uint64_t down_count ;
-
uint64_t up_count ;
-
-
double down_rate ;
-
double up_rate ;
-
-
-
} peer_node_t ;
思维导图如下:
好的,现在回到刚才的问题,BT 项目中的消息传送使用哪一种序列化三方库比较好。
由于消息中多半是流式数据,没有表示层结构体类型的数据类型,所以在这里选择使用 BSON 。
end
阅读(1215) | 评论(0) | 转发(0) |