BitTorrent协议分析五

peer.c文件的头部包含的代码如下:

peer.c

#include <stdio.h>

#include <string.h>

#include <malloc.h>

#include "peer.h"

#include "message.h"

#include "bitfield.h"

 

extern Bitmap *bitmap;

 

// 指向当前与之进行通信的peer链表

Peer *peer_head = NULL;

peer.c中各个函数的定义如下。

ul initialize_peer(Peer *peer)

 

int  initialize_peer(Peer *peer)

{

     if(peer == NULL)   return -1;

     peer->socket = -1;

     memset(peer->ip,0,16);

     peer->port = 0;

     memset(peer->id,0,21);

     peer->state = INITIAL;

    

     peer->in_buff = NULL;

     peer->out_msg = NULL;

     peer->out_msg_copy = NULL;

    

     peer->in_buff = (char *)malloc(MSG_SIZE);

     if(peer->in_buff == NULL)  goto OUT;

     memset(peer->in_buff,0,MSG_SIZE);

     peer->buff_len = 0;

    

     peer->out_msg = (char *)malloc(MSG_SIZE);

     if(peer->out_msg == NULL)  goto OUT;

     memset(peer->out_msg,0,MSG_SIZE);

     peer->msg_len  = 0;

    

     peer->out_msg_copy = (char *)malloc(MSG_SIZE);

     if(peer->out_msg_copy == NULL)  goto OUT;

     memset(peer->out_msg_copy,0,MSG_SIZE);

     peer->msg_copy_len   = 0;

     peer->msg_copy_index = 0;

    

     peer->am_choking = 1;

     peer->am_interested = 0;

     peer->peer_choking = 1;

     peer->peer_interested = 0;

    

     peer->bitmap.bitfield = NULL;

     peer->bitmap.bitfield_length = 0;

     peer->bitmap.valid_length = 0;

    

     peer->Request_piece_head = NULL;

     peer->Requested_piece_head = NULL;

    

     peer->down_total = 0;

     peer->up_total = 0;

    

     peer->start_timestamp = 0;

     peer->recet_timestamp = 0;

    

     peer->last_down_timestamp = 0;

     peer->last_up_timestamp = 0;

     peer->down_count = 0;

     peer->up_count = 0;

     peer->down_rate = 0.0;

     peer->up_rate = 0.0;

    

     peer->next = (Peer *)0;

     return 0;

OUT:

     if(peer->in_buff != NULL)             free(peer->in_buff);

     if(peer->out_msg != NULL)               free(peer->out_msg);

     if(peer->out_msg_copy != NULL)        free(peer->out_msg_copy);

     return -1;

}

ul Peer* add_peer_node()

 

Peer* add_peer_node()

{

     int  ret;

     Peer *node, *p;

    

     // 分配内存空间

     node = (Peer *)malloc(sizeof(Peer));

     if(node == NULL)  {

          printf("%s:%d error\n",__FILE__,__LINE__);

          return NULL;

     }

     // 进行初始化

     ret = initialize_peer(node);

     if(ret < 0) {

          printf("%s:%d error\n",__FILE__,__LINE__);

          free(node);

          return NULL;

     }

     // 将node加入到peer链表中

     if(peer_head == NULL)  { peer_head = node; }  // node为peer链表的第一个结点

     else {

          p = peer_head;   // 使p指针指向peer链表的最后一个结点

          while(p->next != NULL)  p = p->next;

          p->next = node;

     }

     return node;

}

ul int del_peer_node(Peer *peer)

 

int del_peer_node(Peer *peer)

{

     Peer *p = peer_head, *q;

     if(peer == NULL)  return -1;

    

     while(p != NULL) {

          if( p == peer ) {

               if(p == peer_head)  peer_head = p->next;

               else  q->next = p->next;

               free_peer_node(p);

               return 0;

          } else {

               q = p;

               p = p->next;

          }

     }

    

     return -1;

}

ul int cancel_request_list(Peer *node)

// 撤销当前请求队列

int cancel_request_list(Peer *node)

{

Request_piece  *p = node->Request_piece_head;

    

     while(p != NULL) {

          node->Request_piece_head = node->Request_piece_head->next;

          free(p);

          p = node->Request_piece_head;

     }

    

     return 0;

}

ul int cancel_requested_list(Peer *node)

// 撤销当前被请求队列

int cancel_requested_list(Peer *node)

{

Request_piece  * p = node->Requested_piece_head;

    

     while(p != NULL) {

          node->Requested_piece_head = node->Requested_piece_head->next;

          free(p);

          p = node->Requested_piece_head;

     }

    

     return 0;

}

ul void  free_peer_node(Peer *node)

 

void  free_peer_node(Peer *node)

{

     if(node == NULL)  return;

     if(node->bitmap.bitfield != NULL) free(node->bitmap.bitfield);

     if(node->in_buff != NULL)      free(node->in_buff);

     if(node->out_msg != NULL) free(node->out_msg);

     if(node->out_msg_copy != NULL) free(node->out_msg_copy);

     // 撤销请求队列和被请求队列

     cancel_request_list(node);

     cancel_requested_list(node);

     // 释放完peer成员的内存后,再释放peer所占的内存

     free(node);

}

ul void  release_memory_in_peer()

 

void  release_memory_in_peer()

{

Peer *p;

    

     if(peer_head == NULL)  return;

     p = peer_head;

     while(p != NULL) {

          peer_head = peer_head->next;

          free_peer_node(p);

          p = peer_head;

     }

}

ul void print_peers_data()

 

void print_peers_data()

{

     Peer *p = peer_head;

     int  index = 0;

    

     while(p != NULL) {

          printf("peer: %d  IP %s  down_rate: %.2f \n", index, p->ip, p->down_rate);

          index++;

          p = p->next;

     }

}

4.7  消息处理模块的设计和实现

消息处理模块负责根据当前的状态生成并发送消息,接收以及处理消息。消息处理模块由messag.h和message.c两个文件构成,理解和分析本模块的代码时请参考图13-3、图13-4和图13-5。

message.h

#ifndef MESSAGE_H

#define MESSAGE_H

#include "peer.h"

 

int int_to_char(int i, unsigned char c[4]);      // 将整型变量i的4个字节存放到数组c中

int char_to_int(unsigned char c[4]);              // 将数组c中的4个字节转换为一个整型数

 

// 以下函数创建各个类型的消息,创建消息的函数请参考BT协议加以理解

int create_handshake_msg(char *info_hash,char *peer_id,Peer *peer);

int create_keep_alive_msg(Peer *peer);

int create_chock_interested_msg(int type,Peer *peer);

int create_have_msg(int index,Peer *peer);

int create_bitfield_msg(char *bitfield,int bitfield_len,Peer *peer);

int create_request_msg(int index,int begin,int length,Peer *peer);

int create_piece_msg(int index,int begin,char *block,int b_len,Peer *peer);

int create_cancel_msg(int index,int begin,int length,Peer *peer);

int create_port_msg(int port,Peer *peer);

 

// 判断接收缓冲区中是否存放了一条完整的消息

int is_complete_message(unsigned char *buff,unsigned int len,int *ok_len);

// 处理收到的消息,接收缓冲区中存放着一条完整的消息

int parse_response(Peer *peer);

// 处理收到的消息,接收缓冲区中除了存放着一条完整的消息外,还有其他不完整的消息

int parse_response_uncomplete_msg(Peer *p,int ok_len);

// 根据当前的状态创建响应消息

int create_response_message(Peer *peer);

// 为发送have消息作准备,have消息较为特殊,它要发送给所有peer

int prepare_send_have_msg();

// 即将与peer断开时,丢弃套接字发送缓冲区中的所有未发送的消息

void discard_send_buffer(Peer *peer);

 

#endif

message.c文件的头部包括的代码如下:

message.h

#include <stdio.h>

#include <string.h>

#include <malloc.h>

#include <unistd.h>

#include <stdlib.h>

#include <time.h>

#include <string.h>

#include <sys/socket.h>

#include "parse_metafile.h"

#include "bitfield.h"

#include "peer.h"

#include "policy.h"

#include "data.h"

#include "message.h"

 

#define HANDSHAKE   -2  // 握手消息

#define KEEP_ALIVE  -1  // keep_alive消息

#define CHOKE        0   // choke消息

#define UNCHOKE      1   // unchoke消息

#define INTERESTED       2   // interested消息

#define UNINTERESTED    3   // uninterested消息

#define HAVE           4   // have消息

#define BITFIELD     5   // bitfield消息

#define REQUEST      6   // request消息

#define PIECE        7   // piece消息

#define CANCEL       8   // cancel消息

#define PORT           9   // port消息

 

// 如果45秒未给某peer发送消息,则发送keep_alive消息

#define KEEP_ALIVE_TIME 45

 

extern Bitmap   *bitmap;              // 在bitmap.c中定义,指向己方的位图

extern char       info_hash[20];      // 在parse_metafile.c中定义,存放info_hash

extern char       peer_id[20];        // 在parse_metafile.c中定义,存放peer_id

extern int   have_piece_index[64];  // 在data.c中定义,存放下载到的piece的index

extern Peer      *peer_head;         // 在peer.c中定义,指向peer链表

message.c中各个函数的定义如下:

ul int int_to_char(int i, unsigned char c[4])

 

int int_to_char(int i, unsigned char c[4])

{

     c[3] = i%6;

     c[2] = (i-c[3])/256%6;

     c[1] = (i-c[3]-c[2]*256)/256/256%6;

     c[0] = (i-c[3]-c[2]*256-c[1]*256*256)/256/256/256%6;

    

     return 0;

}

程序说明。

假设i = 123456789,若以16进制表示该数为0x75BCD15,则c[0] = 07,c[1] = 5B,c[2] = CD,c[3] = 15。函数char_to_int的功能与本函数刚好相反。

ul int char_to_int(unsigned char c[4])

 

int char_to_int(unsigned char c[4])

{

     int i;

    

     i = c[0]*256*256*256 + c[1]*256*256 + c[2]*256 + c[3];

    

     return i;

}

ul int create_handshake_msg(char *info_hash,char *peer_id,Peer *peer)

 

参数:info_hash在parse_metafile.c中由种子文件计算而得;peer_id也在parse_metafile.c中生成;peer为要发送握手消息给某一个peer的指针变量。

返回:创建消息成功返回0,创建失败返回-1。函数实现代码如下:

int create_handshake_msg(char *info_hash,char *peer_id,Peer *peer)

{

     int           i;

     unsigned char  keyword[20] = "BitTorrent protocol", c = 0x00;

     unsigned char  *buffer = peer->out_msg + peer->msg_len;

     int           len = MSG_SIZE - peer->msg_len;

    

     if(len < 68)  return -1;  // 握手消息的长度固定为68字节

    

     buffer[0] = 19;

     for(i = 0; i < 19; i++)       buffer[i+1]  = keyword[i];

     for(i = 0; i < 8;  i++)       buffer[i+20] = c;

     for(i = 0; i < 20; i++)       buffer[i+28] = info_hash[i];

     for(i = 0; i < 20; i++)       buffer[i+48] = peer_id[i];

    

     peer->msg_len += 68;

     return 0;

}

程序说明。

将生成的握手消息存放在peer结点的发送缓冲区中(即msg_out),其中msg_out[0]~msg_out[msg_len-1]已存放了其他消息。函数中变量len指明缓冲区还有多少空闲。初始情况下,msg_len的值应为0。

ul int create_keep_alive_msg(Peer *peer)

 

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 < 4)  return -1;  // keep_alive消息的长度固定为4

     memset(buffer,0,4);

     peer->msg_len += 4;

     return 0;

}

ul int create_chock_interested_msg(int type,Peer *peer)

 

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;

    

     // choke、unchoke、interested、uninterested消息的长度固定为5

     if(len < 5)  return -1;

     memset(buffer,0,5);

     buffer[3] = 1;

     buffer[4] = type;

    

     peer->msg_len += 5;

     return 0;

}

程序说明。

该函数可创建4种消息,即choke、unchoke、interested、uninterested消息。choke消息的type值为0,unchoke消息为1,interested消息为2,uninterested消息为3。

ul int create_have_msg(int index,Peer *peer)

 

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[4];

    

     if(len < 9)  return -1;      // have消息的长度固定为9

     memset(buffer,0,9);    

     buffer[3] = 5;

     buffer[4] = 4;

     int_to_char(index,c);       // index为piece的下标

     buffer[5] = c[0];

     buffer[6] = c[1];

     buffer[7] = c[2];

     buffer[8] = c[3];

    

     peer->msg_len += 9;

     return 0;

}

ul int create_bitfield_msg(char *bitfield,int bitfield_len,Peer *peer)

 

int create_bitfield_msg(char *bitfield,int bitfield_len,Peer *peer)

{

     int            i;

     unsigned char  c[4];

     unsigned char  *buffer = peer->out_msg + peer->msg_len;

     int            len = MSG_SIZE - peer->msg_len;

    

     if( len < bitfield_len+5 )  {  // bitfield消息的长度为bitfield_len+5

          printf("%s:%d buffer too small\n",__FILE__,__LINE__);

          return -1;

     }

     int_to_char(bitfield_len+1,c); // 位图消息的负载长度为位图长度加1

     for(i = 0; i < 4; i++)  buffer[i] = c[i];

     buffer[4] = 5;

     for(i = 0; i < bitfield_len; i++) buffer[i+5] = bitfield[i];

    

     peer->msg_len += bitfield_len+5; 

     return 0;

}

ul int create_request_msg(int index,int begin,int length,Peer *peer)

功能:创建数据请求消息。

参数:index为请求的piece的下标;begin为piece内的偏移量;length为请求数据的长度。函数实现的代码如下:

int create_request_msg(int index,int begin,int length,Peer *peer)

{

     int            i;

     unsigned char  c[4];

     unsigned char  *buffer = peer->out_msg + peer->msg_len;

     int            len = MSG_SIZE - peer->msg_len;

    

     if(len < 17)  return -1;  // request消息的长度固定为17

     memset(buffer,0,17);

     buffer[3] = 13;

     buffer[4] = 6;

     int_to_char(index,c);

     for(i = 0; i < 4; i++)  buffer[i+5]  = c[i];

     int_to_char(begin,c);

     for(i = 0; i < 4; i++)  buffer[i+9]  = c[i];

     int_to_char(length,c);

     for(i = 0; i < 4; i++)  buffer[i+13] = c[i];

 

     peer->msg_len += 17;

     return 0;

}

ul int create_piece_msg(int index,int begin,char *block,int b_len,Peer *peer)

功能:创建piece消息。

参数:block指向待发送的数据;b_len为block所指向的数据的长度。函数实现的代码如下:

int create_piece_msg(int index,int begin,char *block,int b_len,Peer *peer)

{

     int            i;

     unsigned char  c[4];

     unsigned char  *buffer = peer->out_msg + peer->msg_len;

     int            len = MSG_SIZE - peer->msg_len;

 

     if( len < b_len+13 ) {  // piece消息的长度为b_len+13

          printf("%s:%d buffer too small\n",__FILE__,__LINE__);

          return -1;

     }

    

     int_to_char(b_len+9,c);

     for(i = 0; i < 4; i++)      buffer[i] = c[i];

     buffer[4] = 7;

     int_to_char(index,c);

     for(i = 0; i < 4; i++)      buffer[i+5] = c[i];

     int_to_char(begin,c);

     for(i = 0; i < 4; i++)      buffer[i+9] = c[i];

     for(i = 0; i < b_len; i++)  buffer[i+13] = block[i];

 

     peer->msg_len += b_len+13; 

     return 0;

}

ul int create_cancel_msg(int index,int begin,int length,Peer *peer)

 

int create_cancel_msg(int index,int begin,int length,Peer *peer)

{

     int            i;

     unsigned char  c[4];

     unsigned char  *buffer = peer->out_msg + peer->msg_len;

     int            len = MSG_SIZE - peer->msg_len;

    

     if(len < 17)  return -1;  // cancel消息的长度固定为17

     memset(buffer,0,17);

     buffer[3] = 13;

     buffer[4] = 8;

     int_to_char(index,c);

     for(i = 0; i < 4; i++)  buffer[i+5]  = c[i];

     int_to_char(begin,c);

     for(i = 0; i < 4; i++)  buffer[i+9]  = c[i];

     int_to_char(length,c);

     for(i = 0; i < 4; i++)  buffer[i+13] = c[i];

    

     peer->msg_len += 17;    

     return 0;

}

ul int create_port_msg(int port,Peer *peer)

 

附注:实际上程序从未发送过本消息,因为根据BT协议,该消息是为那些以DHT的方式获取peer地址的应用程序所准备的。函数实现的代码如下:

int create_port_msg(int port,Peer *peer)

{

     unsigned char   c[4];

     unsigned char  *buffer = peer->out_msg + peer->msg_len;

     int            len = MSG_SIZE - peer->msg_len;

    

     if( len < 7)  return 0;  // port消息的长度固定为7

     memset(buffer,0,7);

     buffer[3] = 3;

     buffer[4] = 9;

     int_to_char(port,c);

     buffer[5] = c[2];

     buffer[6] = c[3];

    

     peer->msg_len += 7;

     return 0;

}

ul int is_complete_message(unsigned char *buff,unsigned int len,int *ok_len)

功能:判断缓冲区中是否存放了一条完整的消息。

参数:buff指向存放消息的缓冲区;len为缓冲区buff的长度;ok_len用于返回缓冲区中完整消息的长度,即buff[0]~buff[ok_len-1]存放着一条或多条完整的消息。函数实现的完整代码如下:

int is_complete_message(unsigned char *buff,unsigned int len,int *ok_len)

{

     unsigned int    i;

     char          btkeyword[20];

     unsigned char  keep_alive[4]      = { 0x0, 0x0, 0x0, 0x0 };

     unsigned char  chocke[5]         = { 0x0, 0x0, 0x0, 0x1, 0x0};

     unsigned char  unchocke[5]       = { 0x0, 0x0, 0x0, 0x1, 0x1};

     unsigned char  interested[5]          = { 0x0, 0x0, 0x0, 0x1, 0x2};

     unsigned char  uninterested[5]      = { 0x0, 0x0, 0x0, 0x1, 0x3};

     unsigned char  have[5]            = { 0x0, 0x0, 0x0, 0x5, 0x4};

     unsigned char  request[5]         = { 0x0, 0x0, 0x0, 0xd, 0x6};

     unsigned char  cancel[5]          = { 0x0, 0x0, 0x0, 0xd, 0x8};

     unsigned char  port[5]            = { 0x0, 0x0, 0x0, 0x3, 0x9};

    

     if(buff==NULL || len<=0 || ok_len==NULL)  return -1;

     *ok_len = 0;

     btkeyword[0] = 19;

     memcpy(&btkeyword[1],"BitTorrent protocol",19);  // BitTorrent协议关键字

    

     unsigned char  c[4];

     unsigned int   length;

     for(i = 0; i < len; ) {

          // 握手、chocke、have等消息的长度是固定的

          if( i+68<=len && memcmp(&buff[i],btkeyword,20)==0 )         i += 68;

          else if( i+4 <=len && memcmp(&buff[i],keep_alive,4)==0 )      i += 4;

          else if( i+5 <=len && memcmp(&buff[i],chocke,5)==0 )          i += 5;

          else if( i+5 <=len && memcmp(&buff[i],unchocke,5)==0 )        i += 5;

          else if( i+5 <=len && memcmp(&buff[i],interested,5)==0 )        i += 5;

          else if( i+5 <=len && memcmp(&buff[i],uninterested,5)==0 )      i += 5;

          else if( i+9 <=len && memcmp(&buff[i],have,5)==0 )            i += 9;

          else if( i+17<=len && memcmp(&buff[i],request,5)==0 )         i += 17;

          else if( i+17<=len && memcmp(&buff[i],cancel,5)==0 )          i += 17;

          else if( i+7 <=len && memcmp(&buff[i],port,5)==0 )             i += 7;

          else if( i+5 <=len && buff[i+4]==5 )  {  // bitfield消息的长度是变化的

               c[0] = buff[i];   c[1] = buff[i+1];

               c[2] = buff[i+2]; c[3] = buff[i+3];

               length = char_to_int(c);    

               // 消息长度占4字节,消息本身占length个字节

               if( i+4+length <= len )  i += 4+length;

               else { *ok_len = i; return -1; }

          }

          else if( i+5 <=len && buff[i+4]==7 )  {  // piece消息的长度也是变化的

               c[0] = buff[i];   c[1] = buff[i+1];

               c[2] = buff[i+2]; c[3] = buff[i+3];

               length = char_to_int(c);

               // 消息长度占4字节,消息本身占length个字节

               if( i+4+length <= len )  i += 4+length;

               else { *ok_len = i; return -1; }

          }

          else {

          // 处理未知类型的消息

               if(i+4 <= len) {

                    c[0] = buff[i];   c[1] = buff[i+1];

                    c[2] = buff[i+2]; c[3] = buff[i+3];

                    length = char_to_int(c);

                    // 消息长度占4字节,消息本身占length个字节

                    if(i+4+length <= len)  { i += 4+length; continue; }

                    else { *ok_len = i; return -1; }

               }

          // 如果不是未知消息类型,则认为目前接收的数据还不是一个完整的消息

               *ok_len = i;

               return -1;

          }

     } // for语句结束

    

     *ok_len = i;

     return 1;

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值