msg.c中

int转化 char[4]  char[4]转化int的函数 如下(有多种方案)

 int int_to_char(int i, unsigned char c[])
{
c[] = i % ;
c[] = (i - c[]) / % ;
c[] = (i - c[] - c[] * ) / / % ;
c[] = (i - c[] - c[] * - c[] * * ) / / / % ; return ;
} int IntToChar(int j, unsigned char c[]) {
int i = ;
for (i = ; i < ; i++) {
c[i] = (j >> ( - i - ) * ) & 0xff;
}
return ;
} int CharToInt1(unsigned char c[]) {
int i = ; unsigned char* p = (unsigned char*)&i;
for (int j = ; j > ; j--) {
memcpy(p, &c[j], sizeof(c[j]));
p++;
}
memcpy(p, &c[], sizeof(c[])); return i;
} int CharToInt(unsigned char c[]) {
int i = ;
for (int j = ; j < ; j++) {
i += c[j];
i <<= ;
}
i += c[]; return i;
} int char_to_int(unsigned char c[])
{
int i; i = c[] * * * + c[] * * + c[] * + c[]; return i;
}

创建握手信息的函数 create_handshake_msg()

握手消息

客户端与一个peer建立TCP连接后,首先向peer发送握手消息,peer收到握手消息后回应一个握手消息。握手消息是一个长度固定为68字节的消息,格式如下:
<pstrlen><pstr><reserved><info_hash><peer_id>

握手消息参数
参数 含义
pstrlen pstr的长度,固定为19
pstr BitTorrent协议的关键字,即”BitTorrent protocol”
reserved 占8字节,用于扩展BT协议。可忽略
info_hash 与发往Tracker的GET请求中的info_hash为同一个值,固定为20字节
peer_id 与发往Tracker的GET请求中的peer_id为同一个值,固定为20字节

info_hash就是种子文件中 4:info 后的种子信息计算的哈希值

 int create_handshake_msg(char *info_hash,char *peer_id,Peer *peer)
{
int i;
unsigned char keyword[] = "BitTorrent protocol", c = 0x00;
unsigned char *buffer = peer->out_msg + peer->msg_len;
int len = MSG_SIZE - peer->msg_len; if(len < ) return -; // 68为握手消息的固定长度 buffer[] = ;
for(i = ; i < ; i++) buffer[i+] = keyword[i];
for(i = ; i < ; i++) buffer[i+] = c;
for(i = ; i < ; i++) buffer[i+] = info_hash[i];
for(i = ; i < ; i++) buffer[i+] = peer_id[i]; peer->msg_len += ;
return ;
}

心跳函数 表明I自己在线 create_chock_interested_msg()

发送4个0

 int create_keep_alive_msg(Peer *peer)
{
unsigned char *buffer = peer->out_msg + peer->msg_len;
int len = MSG_SIZE - peer->msg_len; if(len < ) return -; // 4为keep_alive消息的固定长度 memset(buffer,,);
peer->msg_len += ;
return ;
}

chock 函数 create_chock_interested_msg() 表明对方由于某种原因阻塞 比如贡献度不够? 或者是否感兴趣

结构都是一致的 5字节 第四个为1 第五个表示消息类型(choke、unchoke、interested、uninterested)

 int create_chock_interested_msg(int type,Peer *peer)
{
unsigned char *buffer = peer->out_msg + peer->msg_len;
int len = MSG_SIZE - peer->msg_len; // 5为choke、unchoke、interested、uninterested消息的固定长度
if(len < ) return -; memset(buffer,,);
buffer[] = ;
buffer[] = type; peer->msg_len += ;
return ;
}

表示是否拥有该index的piece函数 create_have_msg()

9个字节 最后4个字节是index的int to char 计算的结果。 第4 5个字节填写 5 4 。这个似乎在协议说明里没看到

 int create_have_msg(int index,Peer *peer)
{
unsigned char *buffer = peer->out_msg + peer->msg_len;
int len = MSG_SIZE - peer->msg_len;
unsigned char c[]; if(len < ) return -; // 9为have消息的固定长度 memset(buffer,,);
buffer[] = ;
buffer[] = ; int_to_char(index,c);
buffer[] = c[];
buffer[] = c[];
buffer[] = c[];
buffer[] = c[]; peer->msg_len += ;
return ;
}

创建位图消息 create_bitfield_msg() 传递本地位图文件消息

长度不定 位图长度+5 前4位是位图长度 int to char 的数组  第五位标记为5  后面是位图

 int create_bitfield_msg(char *bitfield,int bitfield_len,Peer *peer)
{
int i;
unsigned char c[];
unsigned char *buffer = peer->out_msg + peer->msg_len;
int len = MSG_SIZE - peer->msg_len; if( len < bitfield_len+ ) { // bitfield消息的长度为bitfield_len+5
printf("%s:%d buffer too small\n",__FILE__,__LINE__);
return -;
} int_to_char(bitfield_len+,c);
for(i = ; i < ; i++) buffer[i] = c[i];
buffer[] = ;
for(i = ; i < bitfield_len; i++) buffer[i+] = bitfield[i]; peer->msg_len += bitfield_len+;
return ;
}

请求文件的内容函数 create_request_msg()

前5字节固定 第4 5字节填写13 6.后面每4个字节分别填写 index begin end的int to char。

因为peer之间交换数据时以slice(长度为16KB的块)为单位的,因此request消息中length的值一般为16K。最大不超过128K

 int create_request_msg(int index,int begin,int length,Peer *peer)
{
int i;
unsigned char c[];
unsigned char *buffer = peer->out_msg + peer->msg_len;
int len = MSG_SIZE - peer->msg_len; if(len < ) return -; // 17为request消息的固定长度 memset(buffer,,);
buffer[] = ;
buffer[] = ;
int_to_char(index,c);
for(i = ; i < ; i++) buffer[i+] = c[i];
int_to_char(begin,c);
for(i = ; i < ; i++) buffer[i+] = c[i];
int_to_char(length,c);
for(i = ; i < ; i++) buffer[i+] = c[i]; peer->msg_len += ;
return ;
}

创建piece信息 create_piece_msg()

长度为piece长度加13。

头4个字节是总长度 piece_length+13经过 int to char转化。接下来是一个标记7的字节。然后是index begin两个长度int to char.接下来是传输piece

 int create_piece_msg(int index,int begin,char *block,int b_len,Peer *peer)
{
int i;
unsigned char c[];
unsigned char *buffer = peer->out_msg + peer->msg_len;
int len = MSG_SIZE - peer->msg_len; if( len < b_len+ ) { // piece消息的长度为b_len+13
printf("IP:%s len:%d\n",peer->ip,len);
printf("%s:%d buffer too small\n",__FILE__,__LINE__);
return -;
} int_to_char(b_len+,c);
for(i = ; i < ; i++) buffer[i] = c[i];
buffer[] = ;
int_to_char(index,c);
for(i = ; i < ; i++) buffer[i+] = c[i];
int_to_char(begin,c);
for(i = ; i < ; i++) buffer[i+] = c[i];
for(i = ; i < b_len; i++) buffer[i+] = block[i]; peer->msg_len += b_len+;
return ;
}

取消信息  用于取消对某个piece的请求 create_cancel_msg()

固定长度17

第4 5个字节 赋值 13 8. 后面12个字节分别是 index begin length的int to char 数组

 int create_cancel_msg(int index,int begin,int length,Peer *peer)
{
int i;
unsigned char c[];
unsigned char *buffer = peer->out_msg + peer->msg_len;
int len = MSG_SIZE - peer->msg_len; if(len < ) return -; // 17为cancel消息的固定长度 memset(buffer,,);
buffer[] = ;
buffer[] = ;
int_to_char(index,c);
for(i = ; i < ; i++) buffer[i+] = c[i];
int_to_char(begin,c);
for(i = ; i < ; i++) buffer[i+] = c[i];
int_to_char(length,c);
for(i = ; i < ; i++) buffer[i+] = c[i]; peer->msg_len += ;
return ;
}

传递端口信息 固定7个字节 第四五字节 为3 9 第六七个字节为端口 int to char的 后两个字节

 int create_port_msg(int port,Peer *peer)
{
unsigned char c[];
unsigned char *buffer = peer->out_msg + peer->msg_len;
int len = MSG_SIZE - peer->msg_len; if( len < ) return ; // 7为port消息的固定长度 memset(buffer,,);
buffer[] = ;
buffer[] = ;
int_to_char(port,c);
buffer[] = c[];
buffer[] = c[]; peer->msg_len += ;
return ;
}

print_msg_buffer() 打印buffer内容

 // 以十六进制的形式打印消息的内容,用于调试
int print_msg_buffer(unsigned char *buffer, int len)
{
int i; for(i = ; i < len; i++) {
printf("%.2x ",buffer[i]);
if( (i+) % == ) printf("\n");
}
printf("\n"); return ;
}

is_complete_message()检测是否一条完整信息存放在BUF中

首先比对固定头信息的集中类型  比如握手 choke interested have request cancel port  然后检测 bitfield 和piece不定长协议。最后根据头文件信息指定的长度是否吻合判断 是否接收到未知定义还是未结束的消息

 int is_complete_message(unsigned char *buff,unsigned int len,int *ok_len)
{
unsigned int i;
char btkeyword[]; unsigned char keep_alive[] = { 0x0, 0x0, 0x0, 0x0 };
unsigned char chocke[] = { 0x0, 0x0, 0x0, 0x1, 0x0};
unsigned char unchocke[] = { 0x0, 0x0, 0x0, 0x1, 0x1};
unsigned char interested[] = { 0x0, 0x0, 0x0, 0x1, 0x2};
unsigned char uninterested[] = { 0x0, 0x0, 0x0, 0x1, 0x3};
unsigned char have[] = { 0x0, 0x0, 0x0, 0x5, 0x4};
unsigned char request[] = { 0x0, 0x0, 0x0, 0xd, 0x6};
unsigned char cancel[] = { 0x0, 0x0, 0x0, 0xd, 0x8};
unsigned char port[] = { 0x0, 0x0, 0x0, 0x3, 0x9}; if(buff==NULL || len<= || ok_len==NULL) return -;
*ok_len = ; btkeyword[] = ;
memcpy(&btkeyword[],"BitTorrent protocol",); // BitTorrent协议关键字 unsigned char c[];
unsigned int length; for(i = ; i < len; ) {
// 握手、chocke、have等消息的长度是固定的
if( i+<=len && memcmp(&buff[i],btkeyword,)== ) i += ;
else if( i+ <=len && memcmp(&buff[i],keep_alive,)== ) i += ;
else if( i+ <=len && memcmp(&buff[i],chocke,)== ) i += ;
else if( i+ <=len && memcmp(&buff[i],unchocke,)== ) i += ;
else if( i+ <=len && memcmp(&buff[i],interested,)== ) i += ;
else if( i+ <=len && memcmp(&buff[i],uninterested,)== ) i += ;
else if( i+ <=len && memcmp(&buff[i],have,)== ) i += ;
else if( i+<=len && memcmp(&buff[i],request,)== ) i += ;
else if( i+<=len && memcmp(&buff[i],cancel,)== ) i += ;
else if( i+ <=len && memcmp(&buff[i],port,)== ) i += ;
// bitfield消息的长度是变化的
else if( i+ <=len && buff[i+]== ) {
c[] = buff[i]; c[] = buff[i+];
c[] = buff[i+]; c[] = buff[i+];
length = char_to_int(c);
// 消息长度占4字节,消息本身占length个字节
if( i++length <= len ) i += +length;
else { *ok_len = i; return -; }
}
// piece消息的长度也是变化的
else if( i+ <=len && buff[i+]== ) {
c[] = buff[i]; c[] = buff[i+];
c[] = buff[i+]; c[] = buff[i+];
length = char_to_int(c);
// 消息长度占4字节,消息本身占length个字节
if( i++length <= len ) i += +length;
else { *ok_len = i; return -; }
}
else {
// 处理未知类型的消息
if(i+ <= len) {
c[] = buff[i]; c[] = buff[i+];
c[] = buff[i+]; c[] = buff[i+];
length = char_to_int(c);
// 消息长度占4字节,消息本身占length个字节
if(i++length <= len) { i += +length; continue; }
else { *ok_len = i; return -; }
}
// 如果也不是未知消息类型,则认为目前接收的数据还不是一个完整的消息
*ok_len = i;
return -;
}
} *ok_len = i;
return ;
}

process_handshake_msg()处理握手信息

比对info_hash 判断是否需要处理还是丢弃  然后从initial进入到 handshaked状态

 int process_handshake_msg(Peer *peer,unsigned char *buff,int len)
{
if(peer==NULL || buff==NULL) return -; if(memcmp(info_hash,buff+,) != ) {
peer->state = CLOSING;
// 丢弃发送缓冲区中的数据
discard_send_buffer(peer);
clear_btcache_before_peer_close(peer);
close(peer->socket);
return -;
} memcpy(peer->id,buff+,);
(peer->id)[] = '\0'; if(peer->state == INITIAL) {
peer->state = HANDSHAKED;
create_handshake_msg(info_hash,peer_id,peer);
}
if(peer->state == HALFSHAKED) peer->state = HANDSHAKED; peer->start_timestamp = time(NULL);
return ;
}

process_keep_alive_msg() 心跳处理 更新下peer的时间 不做其他处理

 int process_keep_alive_msg(Peer *peer,unsigned char *buff,int len)
{
if(peer==NULL || buff==NULL) return -; peer->start_timestamp = time(NULL);
return ;
}

process_choke_msg处理函数 如果peer未关闭且处于未堵塞状态 更改为堵塞  时间重新计数

 int process_choke_msg(Peer *peer,unsigned char *buff,int len)
{
if(peer==NULL || buff==NULL) return -; if( peer->state!=CLOSING && peer->peer_choking== ) {
peer->peer_choking = ;
peer->last_down_timestamp = ;
peer->down_count = ;
peer->down_rate = ;
} peer->start_timestamp = time(NULL);
return ;
}

process_unchoke_msg 解开堵塞状态 创建interested的pieces

 int process_unchoke_msg(Peer *peer,unsigned char *buff,int len)
{
if(peer==NULL || buff==NULL) return -; if( peer->state!=CLOSING && peer->peer_choking== ) {
peer->peer_choking = ;
if(peer->am_interested == ) create_req_slice_msg(peer);
else {
peer->am_interested = is_interested(&(peer->bitmap), bitmap);
if(peer->am_interested == ) create_req_slice_msg(peer);
else printf("Received unchoke but Not interested to IP:%s \n",peer->ip);
} peer->last_down_timestamp = ;
peer->down_count = ;
peer->down_rate = ;
} peer->start_timestamp = time(NULL);
return ;
}

process_interested_msg更新时间  根据位图更新感兴趣的状态

 int process_interested_msg(Peer *peer,unsigned char *buff,int len)
{
if(peer==NULL || buff==NULL) return -; if( peer->state!=CLOSING && peer->state==DATA ) {
peer->peer_interested = is_interested(bitmap, &(peer->bitmap));
if(peer->peer_interested == ) return -;
if(peer->am_choking == ) create_chock_interested_msg(,peer);
} peer->start_timestamp = time(NULL);
return ;
}

process_uninterested_msg 更新时间 从要求名单中删除peer相关的数据

 int process_uninterested_msg(Peer *peer,unsigned char *buff,int len)
{
if(peer==NULL || buff==NULL) return -; if( peer->state!=CLOSING && peer->state==DATA ) {
peer->peer_interested = ;
cancel_requested_list(peer);
} peer->start_timestamp = time(NULL);
return ;
}

process_have_msg 收到其他peer的have信息 更新该peermap 再次检测是否感兴趣。 无兴趣的点 也有1/3的概率进行回复

 int process_have_msg(Peer *peer,unsigned char *buff,int len)
{
int rand_num;
unsigned char c[]; if(peer==NULL || buff==NULL) return -; srand(time(NULL));
rand_num = rand() % ; if( peer->state!=CLOSING && peer->state==DATA ) {
c[] = buff[]; c[] = buff[];
c[] = buff[]; c[] = buff[];
if(peer->bitmap.bitfield != NULL)
set_bit_value(&(peer->bitmap),char_to_int(c),); if(peer->am_interested == ) {
peer->am_interested = is_interested(&(peer->bitmap), bitmap);
// 由原来的对peer不感兴趣变为感兴趣时,发interested消息
if(peer->am_interested == ) create_chock_interested_msg(,peer);
} else { // 收到三个have则发一个interested消息
if(rand_num == ) create_chock_interested_msg(,peer);
}
} peer->start_timestamp = time(NULL);
return ;
}

process_cancel_msg 放弃请求的处理

遍历记录链表 删除

 int process_cancel_msg(Peer *peer,unsigned char *buff,int len)
{
unsigned char c[];
int index, begin, length; if(peer==NULL || buff==NULL) return -; c[] = buff[]; c[] = buff[];
c[] = buff[]; c[] = buff[];
index = char_to_int(c);
c[] = buff[]; c[] = buff[];
c[] = buff[]; c[] = buff[];
begin = char_to_int(c);
c[] = buff[]; c[] = buff[];
c[] = buff[]; c[] = buff[];
length = char_to_int(c); Request_piece *p, *q;
p = q = peer->Requested_piece_head;
while(p != NULL) {
if( p->index==index && p->begin==begin && p->length==length ) {
if(p == peer->Requested_piece_head)
peer->Requested_piece_head = p->next;
else
q->next = p->next;
free(p);
break;
}
q = p;
p = p->next;
} peer->start_timestamp = time(NULL);
return ;
}

后面的函数 基本都是函数名自解释 看看代码即可

参考

《linux c编程实战》第十三章节btcorrent  及代码

http://www.cnblogs.com/UnGeek/p/6052776.html

https://blog.csdn.net/str999_cn/article/details/78880189

bittorrent 学习(三) MSG的更多相关文章

  1. DjangoRestFramework学习三之认证组件、权限组件、频率组件、url注册器、响应器、分页组件

    DjangoRestFramework学习三之认证组件.权限组件.频率组件.url注册器.响应器.分页组件   本节目录 一 认证组件 二 权限组件 三 频率组件 四 URL注册器 五 响应器 六 分 ...

  2. day91 DjangoRestFramework学习三之认证组件、权限组件、频率组件、url注册器、响应器、分页组件

    DjangoRestFramework学习三之认证组件.权限组件.频率组件.url注册器.响应器.分页组件   本节目录 一 认证组件 二 权限组件 三 频率组件 四 URL注册器 五 响应器 六 分 ...

  3. day 89 DjangoRestFramework学习三之认证组件、权限组件、频率组件、url注册器、响应器、分页组件

    DjangoRestFramework学习三之认证组件.权限组件.频率组件.url注册器.响应器.分页组件   本节目录 一 认证组件 二 权限组件 三 频率组件 四 URL注册器 五 响应器 六 分 ...

  4. day 82 Vue学习三之vue组件

      Vue学习三之vue组件   本节目录 一 什么是组件 二 v-model双向数据绑定 三 组件基础 四 父子组件传值 五 平行组件传值 六 xxx 七 xxx 八 xxx 一 什么是组件 首先给 ...

  5. HTTP学习三:HTTPS

    HTTP学习三:HTTPS 1 HTTP安全问题 HTTP1.0/1.1在网络中是明文传输的,因此会被黑客进行攻击. 1.1 窃取数据 因为HTTP1.0/1.1是明文的,黑客很容易获得用户的重要数据 ...

  6. TweenMax动画库学习(三)

    目录               TweenMax动画库学习(一)            TweenMax动画库学习(二)            TweenMax动画库学习(三)           ...

  7. Struts2框架学习(三) 数据处理

    Struts2框架学习(三) 数据处理 Struts2框架框架使用OGNL语言和值栈技术实现数据的流转处理. 值栈就相当于一个容器,用来存放数据,而OGNL是一种快速查询数据的语言. 值栈:Value ...

  8. 4.机器学习——统计学习三要素与最大似然估计、最大后验概率估计及L1、L2正则化

    1.前言 之前我一直对于“最大似然估计”犯迷糊,今天在看了陶轻松.忆臻.nebulaf91等人的博客以及李航老师的<统计学习方法>后,豁然开朗,于是在此记下一些心得体会. “最大似然估计” ...

  9. [ZZ] 深度学习三巨头之一来清华演讲了,你只需要知道这7点

    深度学习三巨头之一来清华演讲了,你只需要知道这7点 http://wemedia.ifeng.com/10939074/wemedia.shtml Yann LeCun还提到了一项FAIR开发的,用于 ...

随机推荐

  1. LeetCode 101. Symmetric Tree 判断对称树 C++

    Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center). For e ...

  2. python 虚拟环境使用与管理(virtualenv)

    安装虚拟环境 pip install virtualenv 安装虚拟环境管理工具 pip install virtualenvwrapper-win 设置虚拟环境目录(虚拟环境存放位置) 默认创建的虚 ...

  3. 201772020113 李清华《面向对象程序设计(java)》第十五周学习总结

    1.实验目的与要求 (1) 掌握Java应用程序的打包操作: (2) 了解应用程序存储配置信息的两种方法: (3) 掌握基于JNLP协议的java Web Start应用程序的发布方法: (5) 掌握 ...

  4. 魔力Python--斐波那契数列(全)

    1. 斐波那契数列应用广泛,对此数列的更好理解有助于我们算法的更进一步,并降低程序的时间复杂度,提高运行效率. 2. 斐波那契数列的应用(4种): 2.1 排列组合----经典例子:爬楼梯 " ...

  5. 小程序官网CMS开源项目出炉,Weixin-App-CMS 1.0 版本正式发布

    Weixin-App-CMS 是捷微团队开发的微信小程序CMS开源项目,涵盖了微网站的基本功能,能够快速发布简单易用的小程序网站.采用工具“微信web开发”上传小程序,即可快速体验发布体验小程序网站. ...

  6. css样式的书写顺序及原理——很重要!

    记得刚开始学习前端的时候,每次写css样式都是用到什么就在样式表后添加什么,完全没有考虑到样式属性的书写顺序对网页加载代码的影响.后来逐渐才知道正确的样式顺序不仅易于查看,并且也属于css样式优化的一 ...

  7. Django分页器的设置

    Django分页器的设置 有时候在页面中数据有多条时很显然需要进行分页显示,那么在python中django可以这样设置一个分页处理 怎么样去设置呢? 我们要用到  Django  中的  Pagin ...

  8. Java中的char占用几个字节

    目录 1.概述 2.答疑 3.总结 1.概述 网上或书上都说是Java中的char占用2个字节,一直没有深入,直到接触了编码,才对此产生了疑问,今天来深入一下这个问题. 2.答疑 char在设计之初的 ...

  9. gradle问题汇总

    问题:从SVN下载到本地后,gradle无法同步,报错如下:Failed to resolve: support-core-utilsFailed to resolve: support-media- ...

  10. python拼接字符串方法汇总

    python拼接字符串一般有以下几种方法: 1.直接通过(+)操作符拼接 s = 'Hello'+' '+'World'+'!' print(s) 输出结果:Hello World! 这种方式最常用. ...