Chinaunix首页 | 论坛 | 博客
  • 博客访问: 167904
  • 博文数量: 36
  • 博客积分: 1466
  • 博客等级: 上尉
  • 技术积分: 380
  • 用 户 组: 普通用户
  • 注册时间: 2007-04-17 17:43
文章分类

全部博文(36)

分类: 系统运维

2007-05-02 19:16:25

libbt研究笔记-2

作者:writer15     mail:writer15(at)163.com
转载请注明原作者名称,联系方法和出处

 

stream.c模块
--------------------------------------------------------------------------------
这个模块在源文件stream.h中有很好的英语说明了,英文好的朋友可以直接去看。但作为笔记我也把这个模块的分析也记录下来。这个模块封装了socket函数recv/send,并且加入了接收/发送缓冲功能。比较奇怪的是socket描述符的创建和删除并不是在这个模块里。这个模块同样围绕着一个结构进行。

typedef struct kStream {
    int fd; //socket描述符,创建和删除都不在本模块进行。
    int inputready; //作用未明,我搜索源码中所有文件,也没有引用的地方。
    int error; //错误码
    int error_count; //recv()函数返回EAGAIN的次数
    int read_count; //recv了多少个次字
    int write_count; //send了多少个字节
    kStringBuffer ibuf; //recv的缓冲
    kStringBuffer obuf; //send的缓冲
} kStream;

/* 创建一个kStream对象,str为NULL时分配一个新的kStream对象。sd为和本对象关联的socket描述符。 */
kStream* kStream_create( kStream *str, int sd);

/* 消毁一个kStream对象。值的注意的是它的源码
void
kStream_finit( kStream *str) {
    kStringBuffer_finit( &str->ibuf);
    kStringBuffer_finit( &str->obuf);
}
为什么没有free掉自身呢?原因是这个结构只以成员变量存在于别的结构(在peer.h的btPeer结构中)
*/

void kStream_finit( kStream *str);

/* recv()函数的封装函数,同时会更新kStream内的一些成员变量(read_count等) */
int kStream_read( kStream *str, char *buf, int max);

/* send()函数的封装函数,同时会更新kStream内的一些成员变量(write_count等) */
int kStream_write( kStream *str, char *buf, int len);

/* 发送发送缓冲中的数据,返回发送后发送缓冲还有多少个节字未发送。 */
int kStream_flush( kStream* str);

/* 清除发送缓冲中的数据,总是返回0 */
int kStream_clear( kStream* str);

/* 带缓冲功能的发送函数,如果不能一次发送完全部数据就会把未发出的数据放进发送缓冲中。返回发送缓冲中的数据长度,或者出错返回0 */
int kStream_fwrite( kStream *str, char *buf, int len);

/* 接收size节字的数据,如果接收到size个字节的数据就复制到buf中,并且清空接收缓冲。但是当接收的数据长度不足size时不向buf写入数据。成功返回接收了多少个字节,出错返回-1。若要判断出错原因,可以查看kStream.error。*/
int kStream_fread( kStream *str, char *buf, int size) ;

/* 和kStream_fread(...)函数一样,但是无论什么清况也不清空接收缓冲。 */
int kStream_fpeek( kStream *str, char *buf, int size) ;

/* 接收缓冲中的数据长度。 */
int kStream_iqlen( kStream *str);

/* 发送缓冲中的数据长度。 */
int kStream_oqlen( kStream *str);

/*
return str->read_count - str->ibuf.cpos; ??
return str->write_count + str->obuf.cpos; ??
*/

int kStream_in_addr( kStream *str);
int kStream_out_addr( kStream *str);

#endif /* __DR1STREAM__H */




bitset模块
--------------------------------------------------------------------------------
这个模块用来操作一个内存块的每个比特位,用处对应BT协议的bitfield。在这个程序中还有多处用到这个模块来表示其它的信息。如哪个piece在下载,对哪些piece感兴趣,哪些piece已经下载等。

先来看看数据结构

typedef struct kBitSet {
    int nbits; //有多少个比特位
    char *bits; //比特位的缓冲

} kBitSet;

/* 创建一个可以容纳nbits个比特位的kBitSet对象 */
kBitSet* kBitSet_create( kBitSet *set, int nbits) ;

/* 根据orig复制并创建一个kBitSet */
kBitSet* kBitSet_createCopy( kBitSet *set, kBitSet *orig);

/* 销毁一个kBitSet对象 */
void kBitSet_finit( kBitSet *set);

/* 复制len长度的kBitSet对象的缓冲区到bytes */
void kBitSet_readBytes( kBitSet *set, char* bytes, int len) ;

/* 设置kBitSet缓冲中的bit位为1 */
void bs_set( kBitSet *set, int bit) ;

/* 设置kBitSet缓冲指定范围的为1 */
void bs_setRange( kBitSet *set, int start, int endplus1);

/* 设置kBitSet缓冲中的bit位为0 */
void bs_clr( kBitSet *set, int bit) ;

/* kBitSet缓冲中的bit位为0返回0,否则返回1*/
int bs_isSet( kBitSet *set, int bit) ;

/* 返回kBitSet中第一个为0的比特位序号 */
int bs_firstClr( kBitSet *set);

/* 测试kBitSet中的所有比特位是否为1 */
int bs_isFull( kBitSet *set);

/* 测试kBitSet中的所有比特位是否为0 */
int bs_isEmpty( kBitSet *set);

/* 返回kBitSet中比特位为1的数量 */
int bs_countBits( kBitSet *set) ;

/* 随机返回一个存在于peer和intr,但不在req中的piece序号 */
int bs_pickblock( kBitSet *req, kBitSet *peer, kBitSet *intr);
   /*
    * randomly picks a block to get from a peer, without asking for any
    * that have already been requested elsewhere and are listed in 'req'.
    */


/* 返回真,如果存在一个piece它 存在于peer和intr,但不在req中*/
int bs_hasInteresting( kBitSet *req, kBitSet *peer, kBitSet *intr);
   /*
    * returns true iff the peer has at least one block that hasn't
    * already been requested.
    */



segmenter.c模块
--------------------------------------------------------------------------------
BT的协议把一个torrent任务中的所有文件看只看作为一个巨大的数据块,这个巨大的数据块被分成n个大小相同的piece,每个piece又可以分为n个slice。数据交换时是以slice为单位的,这个模块的主要作用就是处理这几种关系,并且把下载回来的数据组织成文件。

先来看看这个模块的数据结构:

/* 代表一个torrent任务中的每个文件的信息。torrent包含多少个文件,就有多少个。 */
typedef struct btFile {
    _int64 start; //文件巨大的数据块中的偏移值
    _int64 len; //文件长度
    char *path; //文件名(不包括其保存路径,只包含.torrent中提供的路径)
} btFile;

/* 代表的是一个正在下载的piece */
typedef struct btPartialPiece {
    struct btPartialPiece *next; //链表指针,可以把一串btPartialPiece链在一起
    int piecenumber; //这个结构所代表的piece的序号
    kBitSet filled; //每个kBitSet的比特位代表piece中的一个字节,用来标识这个piece哪里未下载
    int nextbyteReq;        /* next request from here */ //
    int isdone;            /* 0-filling, 1-retry */
    /* 为0时代表正在下载,如果下载完毕后SHA正确,这个结构会从next链表中删除。
    如果出错,这个字段设置为1,但并不从链表删除。当peer_send_request函数检测到这个字段为1时会重新请求该piece */

    char buffer[0];        /* blocksize - allocation trick when creating piece */ //sizeof(buffer)==0
} btPartialPiece;


/* 代表的是一个torrent任务的文件信息.每任务只有一个 */
typedef struct btFileSet {
    _int64 tsize;        /* total size */ //所有文件大小的总和
    _int64 ul;            /* bytes uploaded */ //上传了多少字节 在peer.c==>process_queue进行累加
    _int64 dl;            /* bytes downloaded */ //下载了多少字节 seg_writebuf()进行累加
    _int64 left;        /* bytes left */ //还有多少个字节未下载 seg_writebuf()进行累加
    kBitSet completed;        /* completed blocks */ //已经完成下载的piece的bitfield
    int nfiles; //torrent中有多少个文件
    int blocksize; //piece大小,除了最后一个piece,其它piece的大小都相同
    btFile** file; //文件信息链表
    int npieces; //torrent中有多少个piece
    char *hashes;        /* SHA data */ //SHA校检数据
    btPartialPiece *partial; //未完成下载的piece的链表。
} btFileSet;


/* 输入piece数目,piece大小,sha hash的数据,创建一个btFileSet对象。每个torrent对应一个。 */
btFileSet* btFileSet_create( btFileSet *fs, int npieces, int blocksize, const char *hashbuf) ;

/* 销毁btFileSet对象*/
void btFileSet_destroy( btFileSet *fs);

/* 将一个文件信息添加到btFileSet中,文件信息是.torrent中对应的 */
int btFileSet_addfile( btFileSet *fs, const char *path, _int64 len) ;

/* 输入piece序号,返回相关的btFile文件信息 */
btFile *seg_findFile( btFileSet *fs, int piece);

/* 返回指定piece的长度 */
int seg_piecelen( btFileSet *fs, int piece);

/* finds or allocates the given piece */
/* 在btFileSet的partial链表中查找piecenumber为piece的btPartialPiece并返回。
   若找不到,创建一个piecenumber为piece的btPartialPiece并返回。 */

btPartialPiece *seg_getPiece( btFileSet *fs, int piece);

/* 将参数所标识的数据,写入fs结构中对应文件的对应位置。 */
int seg_writebuf( btFileSet *fs, int piece, int offset, char *buf, int len) ;

/* 从fs中读取参数所标识数据,放到buf中。 */
int seg_readbuf( btFileSet *fs, int piece, int start, char *buf, int len) ;

/* 重新对piece的数据进行SHA校检 */
int seg_review( btFileSet *fs, int piece);

/* 将filename对应的piece设置成1, 返回到interest中。 */
void seg_markFile( btFileSet *fs, char *filename, kBitSet *interest);


关于这个模块是如何运何的,我也只能说说大体上的东西,毕竟项目不是我写的,同时也很复杂。

每个torrent下载任务只有一个btFileSet,在程序的某处用btFileSet_create(...)创建了btFileSet对象,并初始化了它的一些成员变量。之后还调用了btFileSet_addfile(...)添加文件信息到里面。下一步是对下载了的数据进行SHA校检,在btFileSet.complete中标识出哪些piece下载了。

最后来说一下btFileSet.partial。
1. 当本地peer决定发送一个未请求过的piece的请求时,兜兜转转会调用seg_getPiece(...)函数来要求返回一个btPartialPiece结构,seg_getPiece(...)函数malloc后也趁机把它连接到btFileSet.partial链表的未端。
2. 之后是根据返回的btPartialPiece进行发送request
3. 进行数据的接收, 调用seg_writebuf(...)保存数据
4. 最后当下载完毕就对这个piece进行SHA校检,设置相关数据结构,标识这个piece已经下载。再把这个piece的btPartialPiece结构从btFileSet.partial链表中删除。

上面的过程对应下面的函数
1, 2: peer_send_request(...)
3: recv_peermsg(...)的BT_MSG_PIECE消息处理部分
4: seg_writebuf(...)
    
大概就是这样,嘿嘿。

阅读(2181) | 评论(0) | 转发(0) |
0

上一篇:libbt研究笔记-1

下一篇:libbt研究笔记-3

给主人留下些什么吧!~~