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 }
创建握手信息的函数 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 }
心跳函数 表明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 }
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 }
表示是否拥有该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 }
创建位图消息 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 }
请求文件的内容函数 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 }
创建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 }
取消信息 用于取消对某个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 }
传递端口信息 固定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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
后面的函数 基本都是函数名自解释 看看代码即可
参考
《linux c编程实战》第十三章节btcorrent 及代码
http://www.cnblogs.com/UnGeek/p/6052776.html
https://blog.csdn.net/str999_cn/article/details/78880189
欢迎转帖 请保持文本完整并注明出处
技术博客 http://www.cnblogs.com/itdef/
B站算法视频题解
https://space.bilibili.com/18508846
qq 151435887
gitee https://gitee.com/def/
欢迎c c++ 算法爱好者 windows驱动爱好者 服务器程序员沟通交流
如果觉得不错,欢迎点赞,你的鼓励就是我的动力


【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话