bittorrent 学习(三) MSG

msg.c中

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

 1 int int_to_char(int i, unsigned char c[4])
 2 {
 3     c[3] = i % 256;
 4     c[2] = (i - c[3]) / 256 % 256;
 5     c[1] = (i - c[3] - c[2] * 256) / 256 / 256 % 256;
 6     c[0] = (i - c[3] - c[2] * 256 - c[1] * 256 * 256) / 256 / 256 / 256 % 256;
 7 
 8     return 0;
 9 }
10 
11 int IntToChar(int j, unsigned char c[4]) {
12     int i = 0;
13     for (i = 0; i < 4; i++) {
14         c[i] = (j >> (4 - i - 1) * 8) & 0xff;
15     }
16     return 0;
17 }
18 
19 int CharToInt1(unsigned char c[4]) {
20     int i = 0;
21 
22     unsigned char* p = (unsigned char*)&i;
23     for (int j = 3; j > 0 ; j--) {
24         memcpy(p, &c[j], sizeof(c[j]));
25         p++;
26     }
27     memcpy(p, &c[0], sizeof(c[0]));
28 
29     return i;
30 }
31 
32 int CharToInt(unsigned char c[4]) {
33     int i = 0;
34     for (int j = 0; j < 3; j++) {
35         i += c[j];
36         i <<= 8;
37     }
38     i += c[3];
39 
40     return i;
41 }
42 
43 int char_to_int(unsigned char c[4])
44 {
45     int i;
46 
47     i = c[0] * 256 * 256 * 256 + c[1] * 256 * 256 + c[2] * 256 + c[3];
48 
49     return i;
50 }
View Code

 

创建握手信息的函数 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 后的种子信息计算的哈希值

 1 int create_handshake_msg(char *info_hash,char *peer_id,Peer *peer)
 2 {
 3     int            i;
 4     unsigned char  keyword[20] = "BitTorrent protocol", c = 0x00;
 5     unsigned char  *buffer = peer->out_msg + peer->msg_len;
 6     int            len = MSG_SIZE - peer->msg_len;
 7 
 8     if(len < 68)  return -1;  // 68为握手消息的固定长度
 9 
10     buffer[0] = 19;
11     for(i = 0; i < 19; i++)  buffer[i+1]  = keyword[i];
12     for(i = 0; i < 8;  i++)  buffer[i+20] = c;
13     for(i = 0; i < 20; i++)  buffer[i+28] = info_hash[i];
14     for(i = 0; i < 20; i++)  buffer[i+48] = peer_id[i];
15 
16     peer->msg_len += 68;
17     return 0;
18 }
View Code

 

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

发送4个0

 1 int create_keep_alive_msg(Peer *peer)
 2 {
 3     unsigned char  *buffer = peer->out_msg + peer->msg_len;
 4     int            len = MSG_SIZE - peer->msg_len;
 5 
 6     if(len < 4)  return -1;  // 4为keep_alive消息的固定长度
 7 
 8     memset(buffer,0,4);
 9     peer->msg_len += 4;
10     return 0;
11 }
View Code

 

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

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

 1 int create_chock_interested_msg(int type,Peer *peer)
 2 {
 3     unsigned char  *buffer = peer->out_msg + peer->msg_len;
 4     int            len = MSG_SIZE - peer->msg_len;
 5 
 6     // 5为choke、unchoke、interested、uninterested消息的固定长度
 7     if(len < 5)  return -1;
 8 
 9     memset(buffer,0,5);
10     buffer[3] = 1;
11     buffer[4] = type;
12 
13     peer->msg_len += 5;
14     return 0;
15 }
View Code

 

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

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

 1 int create_have_msg(int index,Peer *peer)
 2 {
 3     unsigned char  *buffer = peer->out_msg + peer->msg_len;
 4     int            len = MSG_SIZE - peer->msg_len;
 5     unsigned char  c[4];
 6 
 7     if(len < 9)  return -1;  // 9为have消息的固定长度
 8     
 9     memset(buffer,0,9);    
10     buffer[3] = 5;
11     buffer[4] = 4;
12     
13     int_to_char(index,c);
14     buffer[5] = c[0];
15     buffer[6] = c[1];
16     buffer[7] = c[2];
17     buffer[8] = c[3];
18     
19     peer->msg_len += 9;
20     return 0;
21 }
View Code

 

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

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

 1 int create_bitfield_msg(char *bitfield,int bitfield_len,Peer *peer)
 2 {
 3     int            i;
 4     unsigned char  c[4];
 5     unsigned char  *buffer = peer->out_msg + peer->msg_len;
 6     int            len = MSG_SIZE - peer->msg_len;
 7 
 8     if( len < bitfield_len+5 )  {  // bitfield消息的长度为bitfield_len+5
 9         printf("%s:%d buffer too small\n",__FILE__,__LINE__); 
10         return -1;
11     }
12 
13     int_to_char(bitfield_len+1,c);
14     for(i = 0; i < 4; i++)  buffer[i] = c[i];
15     buffer[4] = 5;
16     for(i = 0; i < bitfield_len; i++) buffer[i+5] = bitfield[i];
17 
18     peer->msg_len += bitfield_len+5;  
19     return 0;
20 }
View Code

 

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

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

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

 1 int create_request_msg(int index,int begin,int length,Peer *peer)
 2 {
 3     int            i;
 4     unsigned char  c[4];
 5     unsigned char  *buffer = peer->out_msg + peer->msg_len;
 6     int            len = MSG_SIZE - peer->msg_len;
 7 
 8     if(len < 17)  return -1;  // 17为request消息的固定长度
 9 
10     memset(buffer,0,17);
11     buffer[3] = 13;
12     buffer[4] = 6;
13     int_to_char(index,c);
14     for(i = 0; i < 4; i++)  buffer[i+5]  = c[i];
15     int_to_char(begin,c);
16     for(i = 0; i < 4; i++)  buffer[i+9]  = c[i];
17     int_to_char(length,c);
18     for(i = 0; i < 4; i++)  buffer[i+13] = c[i];
19 
20     peer->msg_len += 17;
21     return 0;
22 }
View Code

 

 创建piece信息 create_piece_msg()

长度为piece长度加13。

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

 1 int create_piece_msg(int index,int begin,char *block,int b_len,Peer *peer)
 2 {
 3     int            i;
 4     unsigned char  c[4];
 5     unsigned char  *buffer = peer->out_msg + peer->msg_len;
 6     int            len = MSG_SIZE - peer->msg_len;
 7 
 8     if( len < b_len+13 ) {  // piece消息的长度为b_len+13
 9         printf("IP:%s len:%d\n",peer->ip,len);
10         printf("%s:%d buffer too small\n",__FILE__,__LINE__); 
11         return -1;
12     }
13     
14     int_to_char(b_len+9,c);
15     for(i = 0; i < 4; i++)      buffer[i]    = c[i];
16     buffer[4] = 7;
17     int_to_char(index,c);
18     for(i = 0; i < 4; i++)      buffer[i+5]  = c[i];
19     int_to_char(begin,c);
20     for(i = 0; i < 4; i++)      buffer[i+9]  = c[i];
21     for(i = 0; i < b_len; i++)  buffer[i+13] = block[i];
22 
23     peer->msg_len += b_len+13;  
24     return 0;
25 }
View Code

 

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

固定长度17

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

 1 int create_cancel_msg(int index,int begin,int length,Peer *peer)
 2 {
 3     int            i;
 4     unsigned char  c[4];
 5     unsigned char  *buffer = peer->out_msg + peer->msg_len;
 6     int            len = MSG_SIZE - peer->msg_len;
 7     
 8     if(len < 17)  return -1;  // 17为cancel消息的固定长度
 9     
10     memset(buffer,0,17);
11     buffer[3] = 13;
12     buffer[4] = 8;
13     int_to_char(index,c);
14     for(i = 0; i < 4; i++)  buffer[i+5]  = c[i];
15     int_to_char(begin,c);
16     for(i = 0; i < 4; i++)  buffer[i+9]  = c[i];
17     int_to_char(length,c);
18     for(i = 0; i < 4; i++)  buffer[i+13] = c[i];
19 
20     peer->msg_len += 17;    
21     return 0;
22 }
View Code

 

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

 1 int create_port_msg(int port,Peer *peer)
 2 {
 3     unsigned char  c[4];
 4     unsigned char  *buffer = peer->out_msg + peer->msg_len;
 5     int            len = MSG_SIZE - peer->msg_len;
 6 
 7     if( len < 7)  return 0;  // 7为port消息的固定长度
 8 
 9     memset(buffer,0,7);
10     buffer[3] = 3;
11     buffer[4] = 9;
12     int_to_char(port,c);
13     buffer[5] = c[2];
14     buffer[6] = c[3];
15 
16     peer->msg_len += 7;
17     return 0;
18 }
View Code

 

print_msg_buffer() 打印buffer内容

 1 // 以十六进制的形式打印消息的内容,用于调试
 2 int print_msg_buffer(unsigned char *buffer, int len)
 3 {
 4     int i;
 5 
 6     for(i = 0; i < len; i++) {
 7         printf("%.2x ",buffer[i]);
 8         if( (i+1) % 16 == 0 )  printf("\n");
 9     }
10     printf("\n");
11 
12     return 0;
13 }
View Code

 

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

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

 1 int is_complete_message(unsigned char *buff,unsigned int len,int *ok_len)
 2 {
 3     unsigned int   i;
 4     char           btkeyword[20];
 5 
 6     unsigned char  keep_alive[4]   = { 0x0, 0x0, 0x0, 0x0 };
 7     unsigned char  chocke[5]       = { 0x0, 0x0, 0x0, 0x1, 0x0};
 8     unsigned char  unchocke[5]     = { 0x0, 0x0, 0x0, 0x1, 0x1};
 9     unsigned char  interested[5]   = { 0x0, 0x0, 0x0, 0x1, 0x2};
10     unsigned char  uninterested[5] = { 0x0, 0x0, 0x0, 0x1, 0x3};
11     unsigned char  have[5]         = { 0x0, 0x0, 0x0, 0x5, 0x4};
12     unsigned char  request[5]      = { 0x0, 0x0, 0x0, 0xd, 0x6};
13     unsigned char  cancel[5]       = { 0x0, 0x0, 0x0, 0xd, 0x8};
14     unsigned char  port[5]         = { 0x0, 0x0, 0x0, 0x3, 0x9};
15     
16     if(buff==NULL || len<=0 || ok_len==NULL)  return -1;
17     *ok_len = 0;
18     
19     btkeyword[0] = 19;
20     memcpy(&btkeyword[1],"BitTorrent protocol",19);  // BitTorrent协议关键字
21 
22     unsigned char  c[4];
23     unsigned int   length;
24     
25     for(i = 0; i < len; ) {
26         // 握手、chocke、have等消息的长度是固定的
27         if( i+68<=len && memcmp(&buff[i],btkeyword,20)==0 )         i += 68;
28         else if( i+4 <=len && memcmp(&buff[i],keep_alive,4)==0 )    i += 4;
29         else if( i+5 <=len && memcmp(&buff[i],chocke,5)==0 )        i += 5;
30         else if( i+5 <=len && memcmp(&buff[i],unchocke,5)==0 )      i += 5;
31         else if( i+5 <=len && memcmp(&buff[i],interested,5)==0 )    i += 5;
32         else if( i+5 <=len && memcmp(&buff[i],uninterested,5)==0 )  i += 5;
33         else if( i+9 <=len && memcmp(&buff[i],have,5)==0 )          i += 9;
34         else if( i+17<=len && memcmp(&buff[i],request,5)==0 )       i += 17;
35         else if( i+17<=len && memcmp(&buff[i],cancel,5)==0 )        i += 17;
36         else if( i+7 <=len && memcmp(&buff[i],port,5)==0 )          i += 7;
37         // bitfield消息的长度是变化的
38         else if( i+5 <=len && buff[i+4]==5 )  {
39             c[0] = buff[i];   c[1] = buff[i+1];
40             c[2] = buff[i+2]; c[3] = buff[i+3];
41             length = char_to_int(c);    
42             // 消息长度占4字节,消息本身占length个字节
43             if( i+4+length <= len )  i += 4+length;
44             else { *ok_len = i; return -1; }
45         }
46         // piece消息的长度也是变化的
47         else if( i+5 <=len && buff[i+4]==7 )  {
48             c[0] = buff[i];   c[1] = buff[i+1];
49             c[2] = buff[i+2]; c[3] = buff[i+3];
50             length = char_to_int(c);
51             // 消息长度占4字节,消息本身占length个字节
52             if( i+4+length <= len )  i += 4+length;
53             else { *ok_len = i; return -1; }
54         }
55         else {
56             // 处理未知类型的消息
57             if(i+4 <= len) {
58                 c[0] = buff[i];   c[1] = buff[i+1];
59                 c[2] = buff[i+2]; c[3] = buff[i+3];
60                 length = char_to_int(c);
61                 // 消息长度占4字节,消息本身占length个字节
62                 if(i+4+length <= len)  { i += 4+length; continue; }
63                 else { *ok_len = i; return -1; }
64             }
65             // 如果也不是未知消息类型,则认为目前接收的数据还不是一个完整的消息
66             *ok_len = i;
67             return -1;
68         }
69     }
70     
71     *ok_len = i;
72     return 1;
73 }
View Code

 

 process_handshake_msg()处理握手信息

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

 1 int process_handshake_msg(Peer *peer,unsigned char *buff,int len)
 2 {
 3     if(peer==NULL || buff==NULL)  return -1;
 4 
 5     if(memcmp(info_hash,buff+28,20) != 0) { 
 6         peer->state = CLOSING;
 7         // 丢弃发送缓冲区中的数据
 8         discard_send_buffer(peer);
 9         clear_btcache_before_peer_close(peer);
10         close(peer->socket);
11         return -1;
12     }
13     
14     memcpy(peer->id,buff+48,20);
15     (peer->id)[20] = '\0';
16     
17     if(peer->state == INITIAL) {
18         peer->state = HANDSHAKED;
19         create_handshake_msg(info_hash,peer_id,peer);
20     }
21     if(peer->state == HALFSHAKED)  peer->state = HANDSHAKED;
22 
23     peer->start_timestamp = time(NULL);
24     return 0;
25 }
View Code

 

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

1 int process_keep_alive_msg(Peer *peer,unsigned char *buff,int len)
2 {
3     if(peer==NULL || buff==NULL)  return -1;
4 
5     peer->start_timestamp = time(NULL);
6     return 0;
7 }
View Code

 

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

 1 int process_choke_msg(Peer *peer,unsigned char *buff,int len)
 2 {
 3     if(peer==NULL || buff==NULL)  return -1;
 4 
 5     if( peer->state!=CLOSING && peer->peer_choking==0 ) {
 6         peer->peer_choking = 1;
 7         peer->last_down_timestamp = 0;
 8         peer->down_count          = 0;
 9         peer->down_rate           = 0;
10     }
11 
12     peer->start_timestamp = time(NULL);
13     return 0;
14 }
View Code

 

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

 1 int process_unchoke_msg(Peer *peer,unsigned char *buff,int len)
 2 {
 3     if(peer==NULL || buff==NULL)  return -1;
 4 
 5     if( peer->state!=CLOSING && peer->peer_choking==1 ) {
 6         peer->peer_choking = 0;
 7         if(peer->am_interested == 1)  create_req_slice_msg(peer);
 8         else {
 9             peer->am_interested = is_interested(&(peer->bitmap), bitmap);
10             if(peer->am_interested == 1) create_req_slice_msg(peer);
11             else printf("Received unchoke but Not interested to IP:%s \n",peer->ip);
12         }
13 
14         peer->last_down_timestamp = 0;
15         peer->down_count          = 0;
16         peer->down_rate           = 0;
17     }
18 
19     peer->start_timestamp = time(NULL);
20     return 0;
21 }
View Code

 

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

 1 int process_interested_msg(Peer *peer,unsigned char *buff,int len)
 2 {
 3     if(peer==NULL || buff==NULL)  return -1;
 4 
 5     if( peer->state!=CLOSING && peer->state==DATA ) {
 6         peer->peer_interested = is_interested(bitmap, &(peer->bitmap));
 7         if(peer->peer_interested == 0)  return -1;
 8         if(peer->am_choking == 0) create_chock_interested_msg(1,peer);
 9     }
10 
11     peer->start_timestamp = time(NULL);
12     return 0;
13 }
View Code

 

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

 1 int process_uninterested_msg(Peer *peer,unsigned char *buff,int len)
 2 {
 3     if(peer==NULL || buff==NULL)  return -1;
 4 
 5     if( peer->state!=CLOSING && peer->state==DATA ) {
 6         peer->peer_interested = 0;
 7         cancel_requested_list(peer);
 8     }
 9 
10     peer->start_timestamp = time(NULL);
11     return 0;
12 }
View Code

 

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

 1 int process_have_msg(Peer *peer,unsigned char *buff,int len)
 2 {
 3     int           rand_num;
 4     unsigned char c[4];
 5 
 6     if(peer==NULL || buff==NULL)  return -1;
 7 
 8     srand(time(NULL));
 9     rand_num = rand() % 3;
10 
11     if( peer->state!=CLOSING && peer->state==DATA ) {
12         c[0] = buff[5]; c[1] = buff[6];
13         c[2] = buff[7]; c[3] = buff[8];        
14         if(peer->bitmap.bitfield != NULL)
15             set_bit_value(&(peer->bitmap),char_to_int(c),1);
16 
17         if(peer->am_interested == 0) {
18             peer->am_interested = is_interested(&(peer->bitmap), bitmap);
19             // 由原来的对peer不感兴趣变为感兴趣时,发interested消息
20             if(peer->am_interested == 1) create_chock_interested_msg(2,peer);    
21         } else {  // 收到三个have则发一个interested消息
22             if(rand_num == 0) create_chock_interested_msg(2,peer);
23         }
24     }
25 
26     peer->start_timestamp = time(NULL);
27     return 0;
28 }
View Code

 

process_cancel_msg 放弃请求的处理

遍历记录链表 删除

 1 int process_cancel_msg(Peer *peer,unsigned char *buff,int len)
 2 {
 3     unsigned char c[4];
 4     int           index, begin, length;
 5 
 6     if(peer==NULL || buff==NULL)  return -1;
 7     
 8     c[0] = buff[5];  c[1] = buff[6];
 9     c[2] = buff[7];  c[3] = buff[8];
10     index = char_to_int(c);
11     c[0] = buff[9];  c[1] = buff[10];
12     c[2] = buff[11]; c[3] = buff[12];
13     begin = char_to_int(c);
14     c[0] = buff[13]; c[1] = buff[14];
15     c[2] = buff[15]; c[3] = buff[16];
16     length = char_to_int(c);
17     
18     Request_piece *p, *q;
19     p = q = peer->Requested_piece_head;
20     while(p != NULL) { 
21         if( p->index==index && p->begin==begin && p->length==length ) {
22             if(p == peer->Requested_piece_head) 
23                 peer->Requested_piece_head = p->next;
24             else
25                 q->next = p->next;
26             free(p);
27             break;
28         }
29         q = p;
30         p = p->next;
31     }    
32 
33     peer->start_timestamp = time(NULL);
34     return 0;
35 }
View Code

 

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

 

 

 

 

参考

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

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

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

posted on 2018-11-02 13:50  itdef  阅读(347)  评论(0编辑  收藏  举报

导航