zoukankan      html  css  js  c++  java
  • 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
    ",__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
    ",peer->ip,len);
    10         printf("%s:%d buffer too small
    ",__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("
    ");
     9     }
    10     printf("
    ");
    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] = '';
    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 
    ",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 

    作 者: itdef
    欢迎转帖 请保持文本完整并注明出处
    技术博客 http://www.cnblogs.com/itdef/
    B站算法视频题解
    https://space.bilibili.com/18508846
    qq 151435887
    gitee https://gitee.com/def/
    欢迎c c++ 算法爱好者 windows驱动爱好者 服务器程序员沟通交流
    如果觉得不错,欢迎点赞,你的鼓励就是我的动力
    阿里打赏 微信打赏
  • 相关阅读:
    你不知道的JavaScript(上)作用域与闭包
    csu 1982: 小M的移动硬盘
    csu 1985: 驱R符
    csu 1987: 绚丽的手链
    2017ACM/ICPC广西邀请赛 1007 Duizi and Shunzi
    2017ACM/ICPC广西邀请赛 1005 CS Course
    2017ACM/ICPC广西邀请赛 1004 Covering
    hdu 1209 Clock
    trac中wiki直接显示任务代码
    phpcms中action值的含义
  • 原文地址:https://www.cnblogs.com/itdef/p/9895834.html
Copyright © 2011-2022 走看看