bittorrent 學習(三) MSG

msg.c中html

int轉化 char[4]  char[4]轉化int的函數 以下(有多種方案)linux

 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>socket


握手消息參數
參數 含義
pstrlen pstr的長度,固定爲19
pstr BitTorrent協議的關鍵字,即」BitTorrent protocol」
reserved 佔8字節,用於擴展BT協議。可忽略
info_hash 與發往Tracker的GET請求中的info_hash爲同一個值,固定爲20字節
peer_id 與發往Tracker的GET請求中的peer_id爲同一個值,固定爲20字節ide

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()spa

發送4個0.net

 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() 代表對方因爲某種緣由阻塞 好比貢獻度不夠? 或者是否感興趣  3d

結構都是一致的 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 

相關文章
相關標籤/搜索