BitTorrent协议分析五

论坛 期权论坛 脚本     
匿名网站用户   2020-12-21 08:16   77   0

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; } // nodepeer链表的第一个结点

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.hmessage.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]); // 将整型变量i4个字节存放到数组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中定义,存放下载到的pieceindex

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] = 07c[1] = 5Bc[2] = CDc[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_hashparse_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;

// chokeunchokeinteresteduninterested消息的长度固定为5

if(len < 5) return -1;

memset(buffer,0,5);

buffer[3] = 1;

buffer[4] = type;

peer->msg_len += 5;

return 0;

}

程序说明。

该函数可创建4种消息,即chokeunchokeinteresteduninterested消息。choke消息的type值为0unchoke消息为1interested消息为2uninterested消息为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); // indexpiece的下标

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的下标;beginpiece内的偏移量;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_lenblock所指向的数据的长度。函数实现的代码如下:

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; ) {

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

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 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:1136255
帖子:227251
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP