1. 程式人生 > >linux下c/c++例項之十四c實現的bt軟體下載(記錄)

linux下c/c++例項之十四c實現的bt軟體下載(記錄)

一、簡介

       可能許多人使用過位元彗星(BitComet)、位元精靈(BitSpirit)、迅雷下載過自己喜歡的影片、電視劇、網路遊戲;還有很多人使用過PPLive、PPStream、沸點、QQ直播等免費的網路電視直播軟體線上觀看自己喜歡的影片。所有這些軟體都採用了一種近年來流行起來的協議,BitTorrent協議,簡稱BT協議。
       2003年,年輕的軟體工程師Bram Cohen發明了BitTorrent協議。在短短的時間內,BT協議的通訊流量佔據了網際網路總流量的六成以上。BT協議成為一種新的變革技術,因此也催生了很多BT軟體,如BitComet、BitSpirit、Azureus,PPLive、PPStream。


      如下是使用C語言在Linux環境下開發了一個BT軟體,並介紹BT協議和技術,編譯執行在centos6.6(linux)下。

二、詳解

1、BitTorrent協議

(1)BitTorrent(簡稱BT)是一個檔案分發協議,每個下載者在下載的同時不斷向其他下載者上傳已下載的資料。而在FTP、HTTP協議中,每個下載者從FTP或HTTP伺服器處下載自己所需要的檔案,各個下載者之間沒有互動。當非常多的使用者同時訪問和下載伺服器上的檔案時,由於FTP伺服器的處理能力和頻寬的限制,下載速度會急劇下降,有的使用者根本訪問不了伺服器。BT協議與FTP協議不同,它的特點是下載的人越多下載的速度越快,其原因在於每個下載者將已下載的資料提供給其他下載者下載,它充分利用了使用者的上載頻寬。BT協議通過一定的策略保證上傳的速度越快,下載的速度也越快。
(2)基於BT協議的檔案分發系統的構成
基於BT協議的檔案分發系統由以下幾個實體構成。
(1)一個Web伺服器。
(2)一個種子檔案。
(3)一個Tracker伺服器。
(4)一個原始檔案提供者。
(5)一個網路瀏覽器。
(6)一個或多個下載者。
Web伺服器上儲存著種子檔案,下載者使用網路瀏覽器(如IE瀏覽器)從Web伺服器上下載種子檔案。種子檔案,又稱為元原檔案或metafile,它儲存了共享檔案的一些資訊,如共享檔案的檔名、檔案大小、Tracker伺服器的地址。種子檔案通常很小,一般大小為1GB的共享檔案,其種子檔案不足100KB,種子檔案以.torrent為字尾。Tracker伺服器儲存著當前下載某共享檔案的所有下載者的IP和埠。原始檔案提供者提供完整的共享檔案供其他下載者下載,它也被稱為種子,種子檔案就是提供者使用BT客戶端生成的。每個下載者通過執行BT客戶端軟體下載共享檔案。我們把某個下載者本身稱為客戶端,把其他下載者稱為peer。
BT客戶端下載一個共享檔案的過程是:客戶端首先解析種子檔案,獲取待下載的共享檔案的一些資訊,其中包括Tracker伺服器的地址。然後客戶端連線Tracker獲取當前下載該檔案的所有下載者的IP和埠。之後客戶端根據IP和埠連線其他下載者,從它們那裡下載檔案,同時把自己已下載的部分提供給其他下載者下載。
共享檔案在邏輯上被劃分為大小相同的塊,稱為piece,每個piece的大小通常為256KB。對於共享檔案,檔案的第1位元組到第256K(即262144)位元組為第一個piece,第256K+1位元組到第512K位元組為第二個piece,依此類推。種子檔案中包含有每個piece的hash值。BT協議規定使用Sha1演算法對每個piece生成20位元組的hash值,作為每個piece的指紋。每當客戶端下載完一個piece時,即對該peice使用Sha1演算法計算其hash值,並與種子檔案中儲存的該peice的hash值進行比較,如果一致即表明下載了一個完整而正確的piece。一旦某個piece被下載,該piece即提供給其他peer下載。在實際上傳和下載中,每個piece又被劃分為大小相同的slice,每個slice的大小固定為16KB(16384位元組)。peer之間每次傳輸以slice為單位。
從以上描述可以得知,待開發的BT軟體(即BT客戶端)主要包含以下幾個功能:解析種子檔案獲取待下載的檔案的一些資訊,連線Tracker獲取peer的IP和埠,連線peer進行資料上傳和下載、對要釋出的提供共享檔案製作和生成種子檔案。種子檔案和Tracker的返回資訊都以一種簡單而高效的編碼方式進行編碼,稱為B編碼。客戶端與Tracker交換資訊基於HTTP協議,Tracker本身作為一個Web伺服器存在。客戶端與其他peer採用面向連線的可靠傳輸協議TCP進行通訊。下面將進一步作詳細的介紹。
(3)B編碼
        種子檔案和Tracker的返回資訊都是經過B編碼的。要解析和處理種子檔案以及Tracker的返回資訊,首先要熟悉B編碼的規則。B編碼中有4種類型:字串、整型、列表、字典。
        字串的編碼格式為:<字串的長度>:<字串>,其中<>括號中的內容為必需。例如,有一個字串spam,則經過B編碼後為4:spam。
        整型的編碼格式為:i<十進位制的整型數>e,即B編碼中的整數以i作為起始符,以e作為終結符,i為integer的第一個字母,e為end的第一個字母。例如,整數3,經過B編碼後為i3e,整數−3的B編碼為i−3e,整數0的B編碼為i0e。注意i03e不是合法的B編碼,因為03不是十進位制整數,而是八進位制整數。
        列表的編碼格式為:l<任何合法的型別>e,列表以l為起始符,以e為終結符,中間可以為任何合法的經過B編碼的型別,l為list的第一個字母。例如,列表l4:spam4:eggse表示兩個字串,一個是spam,一個是eggs。
        字典的編碼格式為:d<關鍵字><值>e,字典以d為起始符,以e為終結符,關鍵字是一個經過B編碼的字串,值可以是任何合法的B編碼型別,在d和e之間可以出現多個關鍵字和值對,d是dictionary的第一個字母。例如,d4:spaml3:aaa3:bbbee,它是一個字典,該字典的關鍵字是spam,值是一個列表(以l開始,以e結束),列表中有兩個字串aaa和bbb。又如:d9:publisher3:bob17:publisher-webpage15:www.example.come,它也是一個字典,第一個關鍵字是publisher,對應的值為bob,第二個關鍵字是publisher-webpage,對應的值是www.example.com。

2、BT系統結構設計


整個系統各個模組的功能如下。
(1)種子解析:負責解析種子檔案,從中獲取Tracker伺服器的地址,待下載檔案的檔名和長度,piece長度,各個piece的hash值。
(2)連線Tracker:根據HTTP協議構造獲取Peer地址的請求,與Tracker建立連線,解析Tracker的迴應訊息,從而獲取各個peer的IP地址和埠號。
(3)與peer交換資料:根據peer的IP地址和埠號連線peer、從peer處下載資料並將已下載的資料上傳給peer。
(4)出錯處理:定義整個系統可能出現的錯誤型別,並對錯誤進行處理。
(5)執行日誌:記錄程式執行的日誌,並儲存到檔案中以備檢視和分析。
模組“與peer交換資料”是本系統的核心和主要構成部分,它又可以劃分成如下幾個子模組。
(1)peer管理:系統為每一個已建立TCP連線的peer構造一個peer結構體。該結構體的主要成員有:peer的IP地址和埠號、與該peer進行通訊的套接字、該peer的id、當前所處的狀態、傳送緩衝區、接收緩衝區、資料請求佇列、資料被請求佇列、從該peer處已下載的資料量和向該peer上傳的資料量、下載速度和上傳速度。本模組負責管理peer連結串列,新增和刪除peer結點。
(2)訊息處理:peer與peer之間以傳送和接收訊息的方式進行通訊。本模組負責根據當前的狀態生成併發送訊息,接收並處理訊息。BitTorrent協議共定義了12種訊息,其中對下載和上傳資料最重要的是request訊息和piece訊息。request訊息向peer傳送資料請求,指明請求的是哪個piece的哪個slice。Peer接收到request訊息後根據當前的狀態,決定是否傳送資料給對方。如果允許傳送,則構造piece訊息,資料被封裝在該訊息中。每當下載完一個正確的piece時,就向所有peer傳送have訊息通告已獲得該piece,其他peer如果沒有該piece就可以向peer傳送資料請求,每次請求都是以slice為單位。
(3)緩衝管理:如果下載完一個piece就立即寫入硬碟,這樣會導致頻繁讀寫硬碟,既影響速度(讀寫磁碟要花費較多的時間),又不利於保護硬碟(頻繁讀寫磁碟會使硬碟壽命縮短)。為了解決這個問題,幾乎所有的BT軟體都在程式中增加了一個緩衝管理模組。將下載到的資料先快取起來,等到下載到一定量的資料後再集中寫入硬碟。peer請求一個slice的資料時,先將該slice所在的整個piece讀入到緩衝區中,下次Peer再請求該piece的其他slice時,只常緩衝區中獲取,避免了頻繁讀寫硬碟。本模組負責維護一個16MB的緩衝區(大小可調),將下載到的資料儲存在緩衝區中,並在適當時刻寫入硬碟的檔案中。
(4)點陣圖管理:BT協議採用點陣圖指明當前哪些piece已經下載,哪些piece還沒有下載。每個piece佔一位,值為0表示該piece還未下載到,為1則表明已經下載到該piece。本模組負責管理點陣圖,客戶端與peer建立了連線並進行握手之後,即傳送點陣圖給peer告知已下載到哪些piece,同時也接收對方的點陣圖並將其儲存在Peer結構體中。每下載到一個piece就更新自己的點陣圖,併發送have訊息給所有已建立連線的peer。每當接收到peer發來的have訊息就更新該peer的點陣圖。
(5)策略管理:BT協議的設計者為了保證整體效能而制定了許多策略,這些策略雖然沒有寫入BT協議,但已經成為事實上的標準,BT軟體開發者一般都使用這些策略來保證程式的效能。本部分負責策略的管理,主要是計算各個peer的下載和上傳速度,根據下載速度選擇非阻塞peer,採用隨機演算法選擇優化非阻塞peer,以及實現片斷選擇策略。
(6)訊號處理:在執行過程中,程式可能會接收到一些訊號,如SIGINT、SIGTERM,這些訊號的預設動作是立即終止程式。這並不是所期望的。在程式終止前需要作一些處理,如釋放動態申請的記憶體、關閉檔案描述符、關閉套接字。

3、部分模組程式碼

(1)種子解析模組
parse_metafile.h

#ifndef PARSE_METAFILE
#define PARSE_METAFILE

// 儲存從種子檔案中獲取的tracker的URL
typedef struct _Announce_list {
	char    announce[128];
	struct _Announce_list  *next;
} Announce_list;

// 儲存各個待下載檔案的路徑和長度
typedef struct _Files {
	char    path[256];
	long    length;
	struct _Files *next;
} Files; 

int read_metafile(char *metafile_name);         // 讀取種子檔案
int find_keyword(char *keyword,long *position); // 在種子檔案中查詢某個關鍵詞
int read_announce_list();                       // 獲取各個tracker伺服器的地址
int add_an_announce(char* url);                 // 向tracker列表新增一個URL

int get_piece_length();       // 獲取每個piece的長度,一般為256KB
int get_pieces();             // 讀取各個piece的雜湊值

int is_multi_files();         // 判斷下載的是單個檔案還是多個檔案
int get_file_name();          // 獲取檔名,對於多檔案,獲取的是目錄名
int get_file_length();        // 獲取待下載檔案的總長度
int get_files_length_path();  // 獲取檔案的路徑和長度,對多檔案種子有效

int get_info_hash();          // 由info關鍵詞對應的值計算info_hash               
int get_peer_id();            // 生成peer_id,每個peer都有一個20位元組的peer_id

// 釋放parse_metafile.c中動態分配的記憶體
void release_memory_in_parse_metafile();
// 呼叫本檔案中定義的函式,完成解析種子檔案
int  parse_metafile(char *metafile);

#endif
parse_metafile.c
#include <stdio.h>
#include <ctype.h>
#include <malloc.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "parse_metafile.h"
#include "sha1.h"

char  *metafile_content = NULL; // 儲存種子檔案的內容
long  filesize;                 // 種子檔案的長度

int       piece_length  = 0;    // 每個piece的長度,通常為256KB即262144位元組
char      *pieces       = NULL; // 儲存每個pieces的雜湊值,每個雜湊值為20位元組
int       pieces_length = 0;    // pieces緩衝區的長度

int       multi_file    = 0;    // 指明是單檔案還是多檔案
char      *file_name    = NULL; // 對於單檔案,存放檔名;對於多檔案,存放目錄名
long long file_length   = 0;    // 存放待下載檔案的總長度
Files     *files_head   = NULL; // 只對多檔案種子有效,存放各個檔案的路徑和長度

unsigned char info_hash[20];    // 儲存info_hash的值,連線tracker和peer時使用
unsigned char peer_id[20];      // 儲存peer_id的值,連線peer時使用

Announce_list *announce_list_head = NULL; // 用於儲存所有tracker伺服器的URL


int read_metafile(char *metafile_name)
{
	long  i;
	
	// 以二進位制、只讀的方式開啟檔案
	FILE *fp = fopen(metafile_name,"rb");
	if(fp == NULL) {
		printf("%s:%d can not open file\n",__FILE__,__LINE__);
		return -1;
	}
	
	// 獲取種子檔案的長度
	fseek(fp,0,SEEK_END);
	filesize = ftell(fp);
	if(filesize == -1) {
		printf("%s:%d fseek failed\n",__FILE__,__LINE__);
		return -1;
	}
	
	metafile_content = (char *)malloc(filesize+1);
	if(metafile_content == NULL) {
		printf("%s:%d malloc failed\n",__FILE__,__LINE__);
		return -1;
	}
	
	// 讀取種子檔案的內容到metafile_content緩衝區中
	fseek(fp,0,SEEK_SET);
	for(i = 0; i < filesize; i++)
		metafile_content[i] = fgetc(fp);
	metafile_content[i] = '\0';

	fclose(fp); 

#ifdef DEBUG
	printf("metafile size is: %ld\n",filesize);
#endif	
	
	return 0;
}

int find_keyword(char *keyword,long *position)
{
	long i;

	*position = -1;
	if(keyword == NULL)  return 0;

	for(i = 0; i < filesize-strlen(keyword); i++) {
		if( memcmp(&metafile_content[i], keyword, strlen(keyword)) == 0 ) {
			*position = i;
			return 1;
		}
	}
	
	return 0;
}

int read_announce_list()
{
	Announce_list  *node = NULL;
	Announce_list  *p    = NULL;
	int            len   = 0;
	long           i;

	if( find_keyword("13:announce-list",&i) == 0 ) {
		if( find_keyword("8:announce",&i) == 1 ) {
			i = i + strlen("8:announce");
			while( isdigit(metafile_content[i]) ) {
				len = len * 10 + (metafile_content[i] - '0');
				i++;
			}
			i++;  // 跳過 ':'

			node = (Announce_list *)malloc(sizeof(Announce_list));
			strncpy(node->announce,&metafile_content[i],len);
			node->announce[len] = '\0';
			node->next = NULL;
			announce_list_head = node;
		}
	} 
	else {  // 如果有13:announce-list關鍵詞就不用處理8:announce關鍵詞
		i = i + strlen("13:announce-list");
		i++;         // skip 'l'
		while(metafile_content[i] != 'e') {
			i++;     // skip 'l'
			while( isdigit(metafile_content[i]) ) {
				len = len * 10 + (metafile_content[i] - '0');
				i++;
			}
			if( metafile_content[i] == ':' )  i++;
			else  return -1;

			// 只處理以http開頭的tracker地址,不處理以udp開頭的地址
			if( memcmp(&metafile_content[i],"http",4) == 0 ) {
				node = (Announce_list *)malloc(sizeof(Announce_list));
				strncpy(node->announce,&metafile_content[i],len);
				node->announce[len] = '\0';
				node->next = NULL;

				if(announce_list_head == NULL)
					announce_list_head = node;
				else {
					p = announce_list_head;
					while( p->next != NULL) p = p->next; // 使p指向最後個結點
					p->next = node; // node成為tracker列表的最後一個結點
				}
			}

			i = i + len;
			len = 0;
			i++;    // skip 'e'
			if(i >= filesize)  return -1;
		}	
	}

#ifdef DEBUG
	p = announce_list_head;
	while(p != NULL) {
		printf("%s\n",p->announce);
		p = p->next;
	}
#endif	
	
	return 0;
}

// 連線某些tracker時會返回一個重定向URL,需要連線該URL才能獲取peer
int add_an_announce(char *url)
{
	Announce_list *p = announce_list_head, *q;

	// 若引數指定的URL在tracker列表中已存在,則無需新增
	while(p != NULL) {
		if(strcmp(p->announce,url) == 0)  break;
		p = p->next;
	}
	if(p != NULL)  return 0;

	q = (Announce_list *)malloc(sizeof(Announce_list));
	strcpy(q->announce,url);
	q->next = NULL;
	
	p = announce_list_head;
	if(p == NULL)  { announce_list_head = q; return 1; }
	while(p->next != NULL)  p = p->next;
	p->next = q;
	return 1;
}

int is_multi_files()
{
	long i;

	if( find_keyword("5:files",&i) == 1 ) {
		multi_file = 1;
		return 1;
	}

#ifdef DEBUG
	// printf("is_multi_files:%d\n",multi_file);
#endif

	return 0;
}

int get_piece_length()
{
	long i;

	if( find_keyword("12:piece length",&i) == 1 ) {
		i = i + strlen("12:piece length");  // skip "12:piece length"
		i++;  // skip 'i'
		while(metafile_content[i] != 'e') {
			piece_length = piece_length * 10 + (metafile_content[i] - '0');
			i++;
		}
	} else {
		return -1;
	}

#ifdef DEBUG
	printf("piece length:%d\n",piece_length);
#endif

	return 0;
}

int get_pieces()
{
	long i;

	if( find_keyword("6:pieces", &i) == 1 ) {
		i = i + 8;     // skip "6:pieces"
		while(metafile_content[i] != ':') {
			pieces_length = pieces_length * 10 + (metafile_content[i] - '0');
			i++;
		}
		i++;           // skip ':'
		pieces = (char *)malloc(pieces_length+1);
		memcpy(pieces,&metafile_content[i],pieces_length);
		pieces[pieces_length] = '\0';
	} else {
		return -1;
	}

#ifdef DEBUG
	printf("get_pieces ok\n");
#endif

	return 0;
}

int get_file_name()
{
	long  i;
	int   count = 0;

	if( find_keyword("4:name", &i) == 1 ) {
		i = i + 6;  // skip "4:name"
		while(metafile_content[i] != ':') {
			count = count * 10 + (metafile_content[i] - '0');
			i++;
		}
		i++;        // skip ':' 
		file_name = (char *)malloc(count+1);
		memcpy(file_name,&metafile_content[i],count);
		file_name[count] = '\0';
	} else {
		return -1;
	}

#ifdef DEBUG
	// 由於可能含有中文字元,因此可能打印出亂碼
	// printf("file_name:%s\n",file_name);
#endif

	return 0;
}

int get_file_length()
{
	long i;

	if(is_multi_files() == 1)  {
		if(files_head == NULL)  get_files_length_path();
		Files *p = files_head;
		while(p != NULL) { file_length += p->length; p = p->next; }
	} else {
		if( find_keyword("6:length",&i) == 1 ) {
			i = i + 8;  // skip "6:length"
			i++;        // skip 'i' 
			while(metafile_content[i] != 'e') {
				file_length = file_length * 10 + (metafile_content[i] - '0');
				i++;
			}	
		}
	}
	
#ifdef DEBUG
	printf("file_length:%lld\n",file_length);
#endif

	return 0;
}

int get_files_length_path()
{
	long   i;
	int    length;
	int    count;
	Files  *node  = NULL;
	Files  *p     = NULL;

	if(is_multi_files() != 1) {
		return 0;
	}
	
	for(i = 0; i < filesize-8; i++) {
		if( memcmp(&metafile_content[i],"6:length",8) == 0 )
		{
			i = i + 8;  // skip "6:length"
			i++;        // skip 'i' 
			length = 0;
			while(metafile_content[i] != 'e') {
				length = length * 10 + (metafile_content[i] - '0');
				i++;
			}
			node = (Files *)malloc(sizeof(Files));
			node->length = length;
			node->next = NULL;
			if(files_head == NULL)
				files_head = node;
			else {
				p = files_head;
				while(p->next != NULL) p = p->next;
				p->next = node;
			}
		}
		if( memcmp(&metafile_content[i],"4:path",6) == 0 )
		{
			i = i + 6;  // skip "4:path"
			i++;        // skip 'l'
			count = 0;
			while(metafile_content[i] != ':') {
				count = count * 10 + (metafile_content[i] - '0');
				i++;
			}
			i++;        // skip ':'
			p = files_head;
			while(p->next != NULL) p = p->next;
			memcpy(p->path,&metafile_content[i],count);
			*(p->path + count) = '\0';
		}
	}

#ifdef DEBUG
	// 由於可能含有中文字元,因此可能打印出亂碼
	// p = files_head;
	// while(p != NULL) {
	//	 printf("%ld:%s\n",p->length,p->path);
	//	 p = p->next;
	// }
#endif

	return 0;
}

int get_info_hash()
{
	int   push_pop = 0;
	long  i, begin, end;

	if(metafile_content == NULL)  return -1;

	if( find_keyword("4:info",&i) == 1 ) {
		begin = i+6;  // begin是關鍵字"4:info"對應值的起始下標
	} else {
		return -1;
	}

	i = i + 6;        // skip "4:info"
	for(; i < filesize; )
		if(metafile_content[i] == 'd') { 
			push_pop++;
			i++;
		} else if(metafile_content[i] == 'l') {
			push_pop++;
			i++;
		} else if(metafile_content[i] == 'i') {
			i++;  // skip i
			if(i == filesize)  return -1;
			while(metafile_content[i] != 'e') {
				if((i+1) == filesize)  return -1;
				else i++;
			}
			i++;  // skip e
		} else if((metafile_content[i] >= '0') && (metafile_content[i] <= '9')) {
			int number = 0;
			while((metafile_content[i] >= '0') && (metafile_content[i] <= '9')) {
				number = number * 10 + metafile_content[i] - '0';
				i++;
			}
			i++;  // skip :
			i = i + number;
		} else if(metafile_content[i] == 'e') {
			push_pop--;
			if(push_pop == 0) { end = i; break; }
			else  i++; 
		} else {
			return -1;
		}
	if(i == filesize)  return -1;

	SHA1_CTX context;
	SHA1Init(&context);
	SHA1Update(&context, &metafile_content[begin], end-begin+1);
	SHA1Final(info_hash, &context);

#ifdef DEBUG
	printf("info_hash:");
	for(i = 0; i < 20; i++)  
		printf("%.2x ",info_hash[i]);
	printf("\n");
#endif

	return 0;
}

int get_peer_id()
{
	// 設定產生隨機數的種子
	srand(time(NULL));
	// 生成隨機數,並把其中12位賦給peer_id,peer_id前8位固定為-TT1000-
	sprintf(peer_id,"-TT1000-%12d",rand());

#ifdef DEBUG
	int i;
	printf("peer_id:");
	for(i = 0; i < 20; i++)  printf("%c",peer_id[i]);
	printf("\n");
#endif

	return 0;
}

void release_memory_in_parse_metafile()
{
	Announce_list *p;
	Files         *q;
	
	if(metafile_content != NULL)  free(metafile_content);
	if(file_name != NULL)         free(file_name);
	if(pieces != NULL)            free(pieces);
	
	while(announce_list_head != NULL) {
		p = announce_list_head;
		announce_list_head = announce_list_head->next;
		free(p);
	}

	while(files_head != NULL) {
		q = files_head;
		files_head = files_head->next;
		free(q);
	}
}

int parse_metafile(char *metafile)
{
	int ret;

	// 讀取種子檔案
	ret = read_metafile(metafile);
	if(ret < 0) { printf("%s:%d wrong",__FILE__,__LINE__); return -1; }
	
	// 從種子檔案中獲取tracker伺服器的地址
	ret = read_announce_list();
	if(ret < 0) { printf("%s:%d wrong",__FILE__,__LINE__); return -1; }

	// 判斷是否為多檔案
	ret = is_multi_files();
	if(ret < 0) { printf("%s:%d wrong",__FILE__,__LINE__); return -1; }
	
	// 獲取每個piece的長度,一般為256KB
	ret = get_piece_length();
	if(ret < 0) { printf("%s:%d wrong",__FILE__,__LINE__); return -1; }
	
	// 讀取各個piece的雜湊值
	ret = get_pieces();
	if(ret < 0) { printf("%s:%d wrong",__FILE__,__LINE__); return -1; }
	
	// 獲取要下載的檔名,對於多檔案的種子,獲取的是目錄名
	ret = get_file_name();
	if(ret < 0) { printf("%s:%d wrong",__FILE__,__LINE__); return -1; }

	// 對於多檔案的種子,獲取各個待下載的檔案路徑和檔案長度
	ret = get_files_length_path();
	if(ret < 0) { printf("%s:%d wrong",__FILE__,__LINE__); return -1; }
	
	// 獲取待下載的檔案的總長度
	ret = get_file_length();
	if(ret < 0) { printf("%s:%d wrong",__FILE__,__LINE__); return -1; }

	// 獲得info_hash,生成peer_id
	ret = get_info_hash();
	if(ret < 0) { printf("%s:%d wrong",__FILE__,__LINE__); return -1; }
	ret = get_peer_id();
	if(ret < 0) { printf("%s:%d wrong",__FILE__,__LINE__); return -1; }

	return 0;
}
(2)點陣圖管理模組bitfield.h
#ifndef BITFIELD_H
#define BITFIELD_H

typedef struct _Bitmap {
	unsigned char *bitfield;       // 儲存點陣圖
	int           bitfield_length; // 點陣圖所佔的總位元組數
	int           valid_length;    // 點陣圖有效的總位數,每一位代表一個piece
} Bitmap;

int  create_bitfield();                       // 建立點陣圖,分配記憶體並進行初始化
int  get_bit_value(Bitmap *bitmap,int index); // 獲取某一位的值
int  set_bit_value(Bitmap *bitmap,int index,
				   unsigned char value);      // 設定某一位的值
int  all_zero(Bitmap *bitmap);                // 全部清零
int  all_set(Bitmap *bitmap);                 // 全部設定為1
void release_memory_in_bitfield();            // 釋放bitfield.c中動態分配的記憶體
int  print_bitfield(Bitmap *bitmap);          // 列印點陣圖值,用於除錯

int  restore_bitmap(); // 將點陣圖儲存到檔案中 
                       // 在下次下載時,先讀取該檔案獲取已經下載的進度
int  is_interested(Bitmap *dst,Bitmap *src);  // 擁有點陣圖src的peer是否對擁有
                                              // dst點陣圖的peer感興趣
int  get_download_piece_num(); // 獲取當前已下載到的總piece數

#endif
bitfield.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <malloc.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "parse_metafile.h"
#include "bitfield.h"

extern int  pieces_length;
extern char *file_name;

Bitmap      *bitmap = NULL;         // 指向點陣圖
int         download_piece_num = 0; // 當前已下載的piece數 

// 如果存在一個位圖檔案,則讀點陣圖檔案並把獲取的內容儲存到bitmap
// 如此一來,就可以實現斷點續傳,即上次下載的內容不至於丟失
int create_bitfield()
{
	bitmap = (Bitmap *)malloc(sizeof(Bitmap));
	if(bitmap == NULL) { 
		printf("allocate memory for bitmap fiailed\n"); 
		return -1;
	}

	// pieces_length除以20即為總的piece數
	bitmap->valid_length = pieces_length / 20;
	bitmap->bitfield_length = pieces_length / 20 / 8;
	if( (pieces_length/20) % 8 != 0 )  bitmap->bitfield_length++;

	bitmap->bitfield = (unsigned char *)malloc(bitmap->bitfield_length);
	if(bitmap->bitfield == NULL)  { 
		printf("allocate memory for bitmap->bitfield fiailed\n"); 
		if(bitmap != NULL)  free(bitmap);
		return -1;
	}

	char bitmapfile[64];
	sprintf(bitmapfile,"%dbitmap",pieces_length);
	
	int  i;
	FILE *fp = fopen(bitmapfile,"rb");
	if(fp == NULL) {  // 若開啟檔案失敗,說明開始的是一個全新的下載
		memset(bitmap->bitfield, 0, bitmap->bitfield_length);
	} else {
		fseek(fp,0,SEEK_SET);
		for(i = 0; i < bitmap->bitfield_length; i++)
			(bitmap->bitfield)[i] = fgetc(fp);
		fclose(fp); 
		// 給download_piece_num賦新的初值
		download_piece_num = get_download_piece_num();
	}
	
	return 0;
}

int get_bit_value(Bitmap *bitmap,int index)  
{
	int           ret;
	int           byte_index;
	unsigned char byte_value;
	unsigned char inner_byte_index;

	if(index >= bitmap->valid_length)  return -1;

	byte_index = index / 8;
	byte_value = bitmap->bitfield[byte_index];
	inner_byte_index = index % 8;

	byte_value = byte_value >> (7 - inner_byte_index);
	if(byte_value % 2 == 0) ret = 0;
	else                    ret = 1;

	return ret;
}

int set_bit_value(Bitmap *bitmap,int index,unsigned char v)
{
	int           byte_index;
	unsigned char inner_byte_index;

	if(index >= bitmap->valid_length)  return -1;
	if((v != 0) && (v != 1))   return -1;

	byte_index = index / 8;
	inner_byte_index = index % 8;

	v = v << (7 - inner_byte_index);
	bitmap->bitfield[byte_index] = bitmap->bitfield[byte_index] | v;

	return 0;
}

int all_zero(Bitmap *bitmap)
{
	if(bitmap->bitfield == NULL)  return -1;
	memset(bitmap->bitfield,0,bitmap->bitfield_length);
	return 0;
}
 
int all_set(Bitmap *bitmap)
{
	if(bitmap->bitfield == NULL)  return -1;
	memset(bitmap->bitfield,0xff,bitmap->bitfield_length);
	return 0;	
}

void release_memory_in_bitfield()
{
	if(bitmap->bitfield != NULL) free(bitmap->bitfield);
	if(bitmap != NULL)  free(bitmap);
}

int print_bitfield(Bitmap *bitmap)
{
	int i;

	for(i = 0; i < bitmap->bitfield_length; i++) {
		printf("%.2X ",bitmap->bitfield[i]);
		if( (i+1) % 16 == 0)  printf("\n");
	}
	printf("\n");

	return 0;
}

int restore_bitmap()
{
	int  fd;
	char bitmapfile[64];
	
	if( (bitmap == NULL) || (file_name == NULL) )  return -1;
	
	sprintf(bitmapfile,"%dbitmap",pieces_length);
	fd = open(bitmapfile,O_RDWR|O_CREAT|O_TRUNC,0666);
	if(fd < 0)  return -1;
	
	write(fd,bitmap->bitfield,bitmap->bitfield_length);
	close(fd);
	
	return 0;
}

int is_interested(Bitmap *dst,Bitmap *src)
{
	unsigned char const_char[8] = { 0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};
	unsigned char c1, c2;
	int           i, j;
	
	if( dst==NULL || src==NULL )  return -1;
	if( dst->bitfield==NULL || src->bitfield==NULL )  return -1;
	if( dst->bitfield_length!=src->bitfield_length ||
		dst->valid_length!=src->valid_length )
		return -1;
	
	for(i = 0; i < dst->bitfield_length-1; i++) {
		for(j = 0; j < 8; j++) {
			c1 = (dst->bitfield)[i] & const_char[j];
			c2 = (src->bitfield)[i] & const_char[j];
			if(c1>0 && c2==0) return 1;
		}
	}
	
	j  = dst->valid_length % 8;
	c1 = dst->bitfield[dst->bitfield_length-1];
	c2 = src->bitfield[src->bitfield_length-1];
	for(i = 0; i < j; i++) {
		if( (c1&const_char[i])>0 && (c2&const_char[i])==0 )
			return 1;
	}
	
	return 0;
}
/*  
    以上函式的功能測試程式碼如下:
	測試時可以交換map1.bitfield和map2.bitfield的值或賦其他值

	Bitmap map1, map2;
	unsigned char bf1[2] = { 0xa0, 0xa0 };
	unsigned char bf2[2] = { 0xe0, 0xe0 };
  
	map1.bitfield        = bf1;
	map1.bitfield_length = 2;
	map1.valid_length    = 11;
	map2.bitfield        = bf2;
	map2.bitfield_length = 2;
	map2.valid_length    = 11;
	  
    int ret = is_interested(&map1,&map2);	
	printf("%d\n",ret);
 */

// 獲取當前已下載到的總的piece數
int get_download_piece_num()
{
	unsigned char const_char[8] = { 0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};
	int           i, j;
	
	if(bitmap==NULL || bitmap->bitfield==NULL)  return 0;
	
	download_piece_num =0;

	for(i = 0; i < bitmap->bitfield_length-1; i++) {
		for(j = 0; j < 8; j++) {
			if( ((bitmap->bitfield)[i] & const_char[j]) != 0) 
				download_piece_num++;
		}
	}

	unsigned char c = (bitmap->bitfield)[i]; // c存放點陣圖最後一個位元組
	j = bitmap->valid_length % 8;            // j是點陣圖最後一個位元組的有效位數
	for(i = 0; i < j; i++) {
		if( (c & const_char[i]) !=0 ) download_piece_num++;
	}
		
	return download_piece_num;
}
(3)執行日誌模組log.h
#ifndef  LOG_H
#define  LOG_H
#include <stdarg.h>

// 用於記錄程式的行為
void logcmd(char *fmt,...);

// 開啟日誌檔案
int init_logfile(char *filename);

// 將程式執行日誌記錄到檔案
int logfile(char *file,int line,char *msg);

#endif
log.cpp
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include "log.h"

int logfile_fd = -1;

void logcmd(char *fmt,...)
{
	va_list ap;

	va_start(ap,fmt);
	vprintf(fmt,ap);
	va_end(ap);
}

int init_logfile(char *filename)
{
	logfile_fd = open(filename,O_RDWR|O_CREAT|O_APPEND,0666);
	if(logfile_fd < 0) {
		printf("open logfile failed\n");
		return -1;
	}

	return 0;
}

int logfile(char *file,int line,char *msg)
{
	char buff[256];

	if(logfile_fd < 0)  return -1;

	snprintf(buff,256,"%s:%d %s\n",file,line,msg);
	write(logfile_fd,buff,strlen(buff));
	
	return 0;
}
(4)訊號處理模組
signal_hander.h
#ifndef SIGNAL_HANDER_H
#define SIGNAL_HANDER_H

// 做一些清理工作,如釋放動態分配的記憶體
void do_clear_work();

// 處理一些訊號
void process_signal(int signo);

// 設定訊號處理函式
int set_signal_hander();

#endif
signal_hander.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include "signal_hander.h"
#include "parse_metafile.h"
#include "data.h"
#include "bitfield.h"
#include "peer.h"
#include "tracker.h"
#include "torrent.h"

extern int  download_piece_num;
extern int  *fds;
extern int  fds_len;
extern Peer *peer_head;

void do_clear_work()
{
	// 關閉所有peer的socket
	Peer *p = peer_head;
	while(p != NULL) {
		if(p->state != CLOSING)  close(p->socket);
		p = p->next;
	}

	// 儲存點陣圖
	if(download_piece_num > 0) {
		restore_bitmap();
	}

	// 關閉檔案描述符
	int i;
	for(i = 0; i < fds_len; i++) {
		close(fds[i]);
	}
	
	// 釋放動態分配的記憶體
	release_memory_in_parse_metafile();
	release_memory_in_bitfield();
	release_memory_in_btcache();
	release_memory_in_peer();
	release_memory_in_torrent();

	exit(0);
}

void process_signal(int signo)
{
	printf("Please wait for clear operations\n");
	do_clear_work();
}
	

int set_signal_hander()
{
	if(signal(SIGPIPE,SIG_IGN) == SIG_ERR) {
		perror("can not catch signal:sigpipe\n");
		return -1;
	}
	
	if(signal(SIGINT,process_signal)  == SIG_ERR) {
		perror("can not catch signal:sigint\n");
		return -1;
	}
	
	if(signal(SIGTERM,process_signal) == SIG_ERR) {
		perror("can not catch signal:sigterm\n");
		return -1;
	}
	
	return 0;
}
(5)Peer管理模組
peer.h
#ifndef PEER_H
#define PEER_H

#include <string.h>
#include <time.h>
#include "bitfield.h"

#define  INITIAL              -1  // 表明處於初始化狀態
#define  HALFSHAKED            0  // 表明處於半握手狀態
#define  HANDSHAKED            1  // 表明處於全握手狀態
#define  SENDBITFIELD          2  // 表明處於已傳送點陣圖狀態
#define  RECVBITFIELD          3  // 表明處於已接收點陣圖狀態
#define  DATA                  4  // 表明處於與peer交換資料的狀態
#define  CLOSING               5  // 表明處於即將與peer斷開的狀態

// 傳送和接收緩衝區的大小,16K可以存放一個slice,2K可以存放其他訊息
#define  MSG_SIZE  2*1024+16*1024

typedef struct _Request_piece {
	int     index;                // 請求的piece的索引
	int     begin;                // 請求的piece的偏移
	int     length;               // 請求的長度,一般為16KB
	struct _Request_piece *next;
} Request_piece;

typedef struct  _Peer {
	int            socket;                // 通過該socket與peer進行通訊
	char           ip[16];                // peer的ip地址
	unsigned short port;                  // peer的埠號
	char           id[21];                // peer的id

	int            state;                 // 當前所處的狀態

	int            am_choking;            // 是否將peer阻塞
	int            am_interested;         // 是否對peer感興趣
	int            peer_choking;          // 是否被peer阻塞
	int            peer_interested;       // 是否被peer感興趣

	Bitmap         bitmap;                // 存放peer的點陣圖
	
	char           *in_buff;              // 存放從peer處獲取的訊息
	int            buff_len;              // 快取區in_buff的長度
	char           *out_msg;              // 存放將傳送給peer的訊息
	int            msg_len;               // 緩衝區out_msg的長度
	char           *out_msg_copy;         // out_msg的副本,傳送時使用該緩衝區
	int            msg_copy_len;          // 緩衝區out_msg_copy的長度
	int            msg_copy_index;        // 下一次要傳送的資料的偏移量

	Request_piece  *Request_piece_head;   // 向peer請求資料的佇列
	Request_piece  *Requested_piece_head; // 被peer請求資料的佇列

	unsigned int   down_total;            // 從該peer下載的資料的總和
	unsigned int   up_total;              // 向該peer上傳的資料的總和

	time_t         start_timestamp;       // 最近一次接收到peer訊息的時間
	time_t         recet_timestamp;       // 最近一次傳送訊息給peer的時間

	time_t         last_down_timestamp;   // 最近下載資料的開始時間
	time_t         last_up_timestamp;     // 最近上傳資料的開始時間
	long long      down_count;            // 本計時週期從peer下載的資料的位元組數
	long long      up_count;              // 本計時週期向peer上傳的資料的位元組數
	float          down_rate;             // 本計時週期從peer處下載資料的速度
	float          up_rate;               // 本計時週期向peer處上傳資料的速度

	struct _Peer   *next;                 // 指向下一個Peer結構體
} Peer;

int   initialize_peer(Peer *peer);        // 對peer進行初始化
Peer* add_peer_node();                    // 新增一個peer結點
int   del_peer_node(Peer *peer);          // 刪除一個peer結點
void  free_peer_node(Peer *node);         // 釋放一個peer的記憶體

int   cancel_request_list(Peer *node);    // 撤消當前請求佇列
int   cancel_requested_list(Peer *node);  // 撤消當前被請求佇列

void  release_memory_in_peer();           // 釋放peer.c中的動態分配的記憶體
void  print_peers_data();                 // 列印peer連結串列中某些成員的值,用於除錯

#endif
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;

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;
}

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; }
	else {
		p = peer_head;
		while(p->next != NULL)  p = p->next;
		p->next = node;
	}

	return node;
}

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;
}

// 撤消當前請求佇列
int cancel_request_list(Peer *node)
{
	Request_piece  *p;

	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;
}

// 撤消當前被請求佇列
int cancel_requested_list(Peer *node)
{
	Request_piece  *p;
	
	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;
}

void  free_peer_node(Peer *node)
{
	if(node == NULL)  return;
	if(node->bitmap.bitfield != NULL) {
		free(node->bitmap.bitfield);
		node->bitmap.bitfield = NULL;
	}
	if(node->in_buff != NULL) {
		free(node->in_buff); 
		node->in_buff = NULL;
	}
	if(node->out_msg != NULL) {
		free(node->out_msg);
		node->out_msg = NULL;
	}
	if(node->out_msg_copy != NULL) {
		free(node->out_msg_copy);
		node->out_msg_copy = NULL;
	}

	cancel_request_list(node);
	cancel_requested_list(node);

	// 釋放完peer成員的記憶體後,再釋放peer所佔的記憶體
	free(node);
}

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;
	}
}

void print_peers_data()
{
	Peer *p    = peer_head;
	int  index = 0;

	while(p != NULL) {
		printf("peer: %d  down_rate: %.2f \n", index, p->down_rate);

		index++;
		p = p->next;
	}
}
(6)訊息處理模組
message.h
#ifndef MESSAGE_H
#define MESSAGE_H
#include "peer.h"

int int_to_char(int i, unsigned char c[4]);  // 將整型變數i的四個位元組存放到陣列c中
int char_to_int(unsigned char c[4]);         // 將陣列c中的四個位元組轉換為一個整型數

// 以下函式建立各個型別的訊息
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 print_msg_buffer(unsigned char *buffer, int len);
// 為傳送have訊息作準備,have訊息較為特殊,它要傳送給所有peer
int prepare_send_have_msg();
// 判斷緩衝區中是否存放了一個完整的訊息
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);
// 即將與peer斷開時,丟棄傳送緩衝區中的訊息
void discard_send_buffer(Peer *peer);

#endif
message.c
#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 "data.h"
#include "policy.h"
#include "message.h"

#define HANDSHAKE   -2
#define KEEP_ALIVE  -1
#define CHOKE        0
#define UNCHOKE      1
#define INTERESTED   2
#define UNINTERESTED 3
#define HAVE         4
#define BITFIELD     5
#define REQUEST      6
#define PIECE        7
#define CANCEL       8
#define PORT         9

#define KEEP_ALIVE_TIME 45

extern Bitmap *bitmap;
extern char    info_hash[20];
extern char    peer_id[20];
extern int     have_piece_index[64];
extern Peer   *peer_head;

int int_to_char(int i, unsigned char c[4])
{
	c[3] = i%256;
	c[2] = (i-c[3])/256%256;
	c[1] = (i-c[3]-c[2]*256)/256/256%256;
	c[0] = (i-c[3]-c[2]*256-c[1]*256*256)/256/256/256%256;

	return 0;
}

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;
}

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;
}

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;  // 4為keep_alive訊息的固定長度

	memset(buffer,0,4);
	peer->msg_len += 4;
	return 0;
}

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;

	// 5為choke、unchoke、interested、uninterested訊息的固定長度
	if(len < 5)  return -1;

	memset(buffer,0,5);
	buffer[3] = 1;
	buffer[4] = type;

	peer->msg_len += 5;
	return 0;
}

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;  // 9為have訊息的固定長度
	
	memset(buffer,0,9);	
	buffer[3] = 5;
	buffer[4] = 4;
	
	int_to_char(index,c);
	buffer[5] = c[0];
	buffer[6] = c[1];
	buffer[7] = c[2];
	buffer[8] = c[3];
	
	peer->msg_len += 9;
	return 0;
}

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

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;  // 17為request訊息的固定長度

	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;
}

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("IP:%s len:%d\n",peer->ip,len);
		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;
}

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;  // 17為cancel訊息的固定長度
	
	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;
}

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;  // 7為port訊息的固定長度

	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;
}

// 以十六進位制的形式列印訊息的內容,用於除錯
int print_msg_buffer(unsigned char *buffer, int len)
{
	int i;

	for(i = 0; i < len; i++) {
		printf("%.2x ",buffer[i]);
		if( (i+1) % 16 == 0 )  printf("\n");
	}
	printf("\n");

	return 0;
}

// 判斷緩衝區中是否存放了一條完整的訊息
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; ) {
		// 握手、chocke、have等訊息的長度是固定的
		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;
		// bitfield訊息的長度是變化的
		else if( i+5 <=len && buff[i+4]==5 )  {
			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; }
		}
		// piece訊息的長度也是變化的
		else if( i+5 <=len && buff[i+4]==7 )  {
			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;
		}
	}
	
	*ok_len = i;
	return 1;
}

int process_handshake_msg(Peer *peer,unsigned char *buff,int len)
{
	if(peer==NULL || buff==NULL)  return -1;

	if(memcmp(info_hash,buff+28,20) != 0) { 
		peer->state = CLOSING;
		// 丟棄傳送緩衝區中的資料
		discard_send_buffer(peer);
		clear_btcache_before_peer_close(peer);
		close(peer->socket);
		return -1;
	}
	
	memcpy(peer->id,buff+48,20);
	(peer->id)[20] = '\0';
	
	if(peer->state == INITIAL) {
		peer->state = HANDSHAKED;
		create_handshake_msg(info_hash,peer_id,peer);
	}
	if(peer->state == HALFSHAKED)  peer->state = HANDSHAKED;

	peer->start_timestamp = time(NULL);
	return 0;
}

int process_keep_alive_msg(Peer *peer,unsigned char *buff,int len)
{
	if(peer==NULL || buff==NULL)  return -1;

	peer->start_timestamp = time(NULL);
	return 0;
}

int process_choke_msg(Peer *peer,unsigned char *buff,int len)
{
	if(peer==NULL || buff==NULL)  return -1;

	if( peer->state!=CLOSING && peer->peer_choking==0 ) {
		peer->peer_choking = 1;
		peer->last_down_timestamp = 0;
		peer->down_count          = 0;
		peer->down_rate           = 0;
	}

	peer->start_timestamp = time(NULL);
	return 0;
}

int process_unchoke_msg(Peer *peer,unsigned char *buff,int len)
{
	if(peer==NULL || buff==NULL)  return -1;

	if( peer->state!=CLOSING && peer->peer_choking==1 ) {
		peer->peer_choking = 0;
		if(peer->am_interested == 1)  create_req_slice_msg(peer);
		else {
			peer->am_interested = is_interested(&(peer->bitmap), bitmap);
			if(peer->am_interested == 1) create_req_slice_msg(peer);
			else printf("Received unchoke but Not interested to IP:%s \n",peer->ip);
		}

		peer->last_down_timestamp = 0;
		peer->down_count          = 0;
		peer->down_rate           = 0;
	}

	peer->start_timestamp = time(NULL);
	return 0;
}

int process_interested_msg(Peer *peer,unsigned char *buff,int len)
{
	if(peer==NULL || buff==NULL)  return -1;

	if( peer->state!=CLOSING && peer->state==DATA ) {
		peer->peer_interested = is_interested(bitmap, &(peer->bitmap));
		if(peer->peer_interested == 0)  return -1;
		if(peer->am_choking == 0) create_chock_interested_msg(1,peer);
	}

	peer->start_timestamp = time(NULL);
	return 0;
}

int process_uninterested_msg(Peer *peer,unsigned char *buff,int len)
{
	if(peer==NULL || buff==NULL)  return -1;

	if( peer->state!=CLOSING && peer->state==DATA ) {
		peer->peer_interested = 0;
		cancel_requested_list(peer);
	}

	peer->start_timestamp = time(NULL);
	return 0;
}

int process_have_msg(Peer *peer,unsigned char *buff,int len)
{
	int           rand_num;
	unsigned char c[4];

	if(peer==NULL || buff==NULL)  return -1;

	srand(time(NULL));
	rand_num = rand() % 3;

	if( peer->state!=CLOSING && peer->state==DATA ) {
		c[0] = buff[5]; c[1] = buff[6];
		c[2] = buff[7]; c[3] = buff[8];		
		if(peer->bitmap.bitfield != NULL)
			set_bit_value(&(peer->bitmap),char_to_int(c),1);

		if(peer->am_interested == 0) {
			peer->am_interested = is_interested(&(peer->bitmap), bitmap);
			// 由原來的對peer不感興趣變為感興趣時,發interested訊息
			if(peer->am_interested == 1) create_chock_interested_msg(2,peer);	
		} else {  // 收到三個have則發一個interested訊息
			if(rand_num == 0) create_chock_interested_msg(2,peer);
		}
	}

	peer->start_timestamp = time(NULL);
	return 0;
}

int process_cancel_msg(Peer *peer,unsigned char *buff,int len)
{
	unsigned char c[4];
	int           index, begin, length;

	if(peer==NULL || buff==NULL)  return -1;
	
	c[0] = buff[5];  c[1] = buff[6];
	c[2] = buff[7];  c[3] = buff[8];
	index = char_to_int(c);
	c[0] = buff[9];  c[1] = buff[10];
	c[2] = buff[11]; c[3] = buff[12];
	begin = char_to_int(c);
	c[0] = buff[13]; c[1] = buff[14];
	c[2] = buff[15]; c[3] = buff[16];
	length = char_to_int(c);
	
	Request_piece *p, *q;
	p = q = peer->Requested_piece_head;
	while(p != NULL) { 
		if( p->index==index && p->begin==begin && p->length==length ) {
			if(p == peer->Requested_piece_head) 
				peer->Requested_piece_head = p->next;
			else
				q->next = p->next;
			free(p);
			break;
		}
		q = p;
		p = p->next;
	}	

	peer->start_timestamp = time(NULL);
	return 0;
}

int process_bitfield_msg(Peer *peer,unsigned char *buff,int len)
{
	unsigned char c[4];

	if(peer==NULL || buff==NULL)  return -1;
	if(peer->state==HANDSHAKED || peer->state==SENDBITFIELD) {
		c[0] = buff[0];   c[1] = buff[1];
		c[2] = buff[2];   c[3] = buff[3];			

		if( peer->bitmap.bitfield != NULL ) {
			free(peer->bitmap.bitfield);
			peer->bitmap.bitfield = NULL;
		}
		peer->bitmap.valid_length = bitmap->valid_length;
		if(bitmap->bitfield_length != char_to_int(c)-1) {
			peer->state = CLOSING;
			// 丟棄傳送緩衝區中的資料
			discard_send_buffer(peer);
			clear_btcache_before_peer_close(peer);
			close(peer->socket);
			return -1;
		}
		peer->bitmap.bitfield_length = char_to_int(c) - 1;
		peer->bitmap.bitfield = 
			(unsigned char *)malloc(peer->bitmap.bitfield_length);
		memcpy(peer->bitmap.bitfield,&buff[5],peer->bitmap.bitfield_length);
	
		// 如果原狀態為已握手,收到點陣圖後應該向peer發點陣圖
		if(peer->state == HANDSHAKED) {
			create_bitfield_msg(bitmap->bitfield,bitmap->bitfield_length,peer);
			peer->state = DATA;
		}
		// 如果原狀態為已傳送點陣圖,收到點陣圖後可以準備交換資料
		if(peer->state == SENDBITFIELD) {
			peer->state = DATA;
		}

		// 判斷peer是否對我們感興趣
		peer->peer_interested = is_interested(bitmap,&(peer->bitmap));
		// 判斷對peer是否感興趣,若是則傳送interested訊息
		peer->am_interested = is_interested(&(peer->bitmap), bitmap);
		if(peer->am_interested == 1) create_chock_interested_msg(2,peer);
	}
	
	peer->start_timestamp = time(NULL);
	return 0;
}

int process_request_msg(Peer *peer,unsigned char *buff,int len)
{
	unsigned char  c[4];
	int            index, begin, length;
	Request_piece  *request_piece, *p;
	
	if(peer==NULL || buff==NULL)  return -1;

	if(peer->am_choking==0 && peer->peer_interested==1) {
		c[0] = buff[5];  c[1] = buff[6];
		c[2] = buff[7];  c[3] = buff[8];
		index = char_to_int(c);
		c[0] = buff[9];  c[1] = buff[10];
		c[2] = buff[11]; c[3] = buff[12];
		begin = char_to_int(c);
		c[0] = buff[13]; c[1] = buff[14];
		c[2] = buff[15]; c[3] = buff[16];
		length = char_to_int(c);

		// 錯誤的slice請求
		if( begin%(16*1024) != 0 ) {
			return 0;
		}
		
		// 檢視該請求是否已存在,若已存在,則不進行處理
		p = peer->Requested_piece_head;
		while(p != NULL) {
			if(p->index==index && p->begin==begin && p->length==length) {
				break;
			}
			p = p->next;
		}
		if(p != NULL)  return 0;

		// 將請求加入到請求佇列中
		request_piece = (Request_piece *)malloc(sizeof(Request_piece));
		if(request_piece == NULL)  { 
			printf("%s:%d error",__FILE__,__LINE__); 
			return 0; 
		}
		request_piece->index  = index;
		request_piece->begin  = begin;
		request_piece->length = length;
		request_piece->next   = NULL;
		if( peer->Requested_piece_head == NULL ) 
			peer->Requested_piece_head = request_piece;
		else {
			p = peer->Requested_piece_head;
			while(p->next != NULL)  p = p->next;
			p->next = request_piece;
		}
		//printf("*** add a request FROM IP:%s index:%-6d begin:%-6x ***\n",
		//       peer->ip,index,begin);
	}

	peer->start_timestamp = time(NULL);
	return 0;
}

int process_piece_msg(Peer *peer,unsigned char *buff,int len)
{
	unsigned char  c[4];
	int            index, begin, length;
	Request_piece  *p;

	if(peer==NULL || buff==NULL)  return -1;
	
	if(peer->peer_choking==0) {
		c[0] = buff[0];    c[1] = buff[1];
		c[2] = buff[2];    c[3] = buff[3];
		length = char_to_int(c) - 9;
		c[0] = buff[5];    c[1] = buff[6];
		c[2] = buff[7];    c[3] = buff[8];
		index = char_to_int(c);
		c[0] = buff[9];    c[1] = buff[10];
		c[2] = buff[11];   c[3] = buff[12];
		begin = char_to_int(c);

		p = peer->Request_piece_head;
		while(p != NULL) {
			if(p->index==index && p->begin==begin && p->length==length)
				break;
			p = p->next;
		}
		if(p == NULL) {printf("did not found matched request\n"); return -1;}

		if(peer->last_down_timestamp == 0)
			peer->last_down_timestamp = time(NULL);
		peer->down_count += length;
		peer->down_total += length;

		write_slice_to_btcache(index,begin,length,buff+13,length,peer);

		create_req_slice_msg(peer);
	}

	peer->start_timestamp = time(NULL);
	return 0;
}

int parse_response(Peer *peer)
{
	unsigned char  btkeyword[20];
	unsigned char  keep_alive[4] = { 0x0, 0x0, 0x0, 0x0 };
	int            index;
	unsigned char  *buff = peer->in_buff;
	int            len = peer->buff_len;

	if(buff==NULL || peer==NULL)  return -1;

	btkeyword[0] = 19;
	memcpy(&btkeyword[1],"BitTorrent protocol",19);  // BitTorrent協議關鍵字

	// 分別處理12種訊息
	for(index = 0; index < len; ) {	

		if( (len-index >= 68) && (memcmp(&buff[index],btkeyword,20) == 0) ) {
			process_handshake_msg(peer,buff+index,68);
			index += 68;
		} 
		else if( (len-index >= 4) && (memcmp(&buff[index],keep_alive,4) == 0)){
			process_keep_alive_msg(peer,buff+index,4);
			index += 4; 
		}
		else if( (len-index >= 5) && (buff[index+4] == CHOKE) ) {
			process_choke_msg(peer,buff+index,5);
			index += 5;
		}
		else if( (len-index >= 5) && (buff[index+4] == UNCHOKE) ) {
			process_unchoke_msg(peer,buff+index,5);
			index += 5;
		}
		else if( (len-index >= 5) && (buff[index+4] == INTERESTED) ) {
			process_interested_msg(peer,buff+index,5);
			index += 5;
		}
		else if( (len-index >= 5) && (buff[index+4] == UNINTERESTED) ) {
			process_uninterested_msg(peer,buff+index,5);