位图管理模块的设计和实现
对位图的操作主要在bitfield.h和bitfield.c中,负责创建位图,设置和获取位图某一位的值,保存位图等操作。
//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 printf_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_H
程序说明:结构体Bitmap中,bitfield_length是指针bitfield所指向的内存空间的长度(单位B),而valid_length是位图的有效位数。如:一位图在100B,而有效数位795,那么最后一个字节的最后5位是无效的。
函数is_interested用于判断两个peer是否感兴趣,如果peer1拥有某个piece,而peer2没有,则peer2对peer1感兴趣,希望从peer1出下载它没有的piece。
函数get_download_piece_num用于获取已下载的piece数,其方法是统计结构体Bitmap的bitfield成员所指向的内存中值为1的位数。
文件bitfield.c的头部如下:
#include
#include
#include
#include
#include
#include
#include
#include "parse_metafile.h"
#include "bitfield.h"
extern int pieces_length; //pieces缓冲区的长度
extern char *file_name; //对于单文件,存放文件名;对于多文件,存放目录名
Bitmap *bitmap = NULL; //指向位图
int download_piece_num = 0; //当前已下载的piece数
程序说明:如果要在其他源文件使用另一个源文件中定义的变量,需要在此变量的定义处以extern关键字声明,如“extern int pieces_length”。声明仅仅告知编译器有某个变量,定义是要分配内存空间的。
全局变量bitmap指向自己的位图,可以从位图中获取下载的进度。peer的位图存放在Peer结构体中。
以下是bitfield.c的函数声明。
int create_bitfield()
// 如果存在一个位图文件,则读位图文件并把获取的内容保存到bitmap
// 如此一来,就可以实现断点续传,即上次下载的内容不至于丢失
/*
*功能:创建待下载文件的位图
*传入参数:无
*传出参数:无
*返回值:
* -1 失败
* 1 添加成功
*/
int create_bitfield()
{
bitmap = (Bitmap *)malloc(sizeof(Bitmap));
if(bitmap == NULL)
{
printf("allocate memory for bitmap fiailed\n");
return -1;
}
// pieces_length除以20即为总的piece数(每个piece的hash值长度为20个字节)
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_vale(Bitmap *bitmap, int index)
/*
*功能:获取位图中某一位的值。
*传入参数:无
*传出参数:无
*返回值:
* 位图中某一位的值
* -1 失败
*/
int get_bit_value(Bitmap *bitmap, int index)
{
int ret;
int byte_index;
unsigned char byte_value;
unsigned char inner_byte_index;
//索引值越界
if(bitmap == NULL || 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;
}
为了方便理解,可以假设某位图为2字节(值为1011001101010100),有效位数14,意味着待下载的文件共有14个piece。位图第一个字节指明index为0~7的piece是否已下载,第二个字节指明index为8~13的piece是否下载。现在要判断index为8的piece是否已经下载,也就是获取 位图第二字节最高位的值。
int set_bit_value(Bitmap *bitmap, int index, unsigned char value)
/*
*功能:设置位图中某一位的值
*传入参数:bitmap 等
*传出参数:无
*返回值:
* 0 成功
* -1 失败
*/
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;
//值非0/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)
/*
*功能:将位图所有位清0
*传入参数:bitmap
*传出参数:无
*返回值:
* 0 成功
* -1 失败
*/
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)
/*
*功能:将位图所有位置1
*传入参数:bitmap
*传出参数:无
*返回值:
* 0 成功
* -1 失败
*/
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()
/*
*功能:释放本模块所申请的动态内存
*传入参数:无
*传出参数:无
*返回值:
*/
void release_memory_in_bitfield()
{
if(bitmap->bitfield != NULL) free(bitmap->bitfield);
if(bitmap != NULL) free(bitmap);
}
int print_bitfield(Bitmap *bitmap)
/*
*功能:打印位图用于调试程序
*传入参数:bitmap
*传出参数:无
*返回值:
* 0 成功
*/
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()
/*
*功能:保存位图用于短点续传
*传入参数:wu
*传出参数:无
*返回值:
* 0 成功
* -1 失败
*/
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)
/*
*功能:判断具有src位图的peer对具有dst位图的peer是否感兴趣
*传入参数:src dst
*传出参数:无
*返回值:
* 1 感兴趣
* 0 运行成功
* -1 失败
*/
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;
}
//如果dst中某位为1而src对应为0,则说明src对dst感兴趣
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);
*/
int get_download_piece_num()
// 获取当前已下载到的总的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;
}