libbt研究笔记-1
作者:writer15 mail:writer15(at)163.com
转载请注明原作者名称,联系方法和出处
bts.c模块 /* 2007-4-12 7:48下午 */ -------------------------------------------------------------------------------- #2天没写过文档了,原因是不知怎么表达接下来的几个模块,还有自己的一些事也很烦恼。 bts模块抽象出一套数据流的接口,通过这套接口,你可以对内存流,文件流进行操作,而且不用理会其种类和来源。
先来看看btStream结构
typedef enum { BTS_INPUT, BTS_OUTPUT, BTS_FREE } btsIo; //BTS_FREE未发现有引用的地方
struct btStream; typedef struct btStream { int type; //eq BTFILESTREAM or BTSTRSTREAM
btsIo iodir; int (*read)(struct btStream* bts, char *buf, int len); int (*write)(struct btStream* bts, char *buf, int len); int (*peek)(struct btStream* bts); int (*rewind)(struct btStream* bts, btsIo iodir); void (*destroy)(struct btStream* bts); } btStream;
struct btstrbuf { char *buf; int len; };
一个btStream实例就代表一个数据流对象。 type字段代表是流的类型,libbt中只有文件流和内存流2种。iodir字段到现在我也不太明白有什么用。之后就是5个函数指针,当流的类型不同的,它们指向不同的函数。 比如文件流,这5个指针就指向操作文件流的5个函数,内存流也一样。在创建流的时候指定类型和这5个函数,之后流接口函数就可以以相同的函数处理不同的流。
文件流和内存流的实现函数我就不多说了,看源码吧很简单。
下面说说这个模块输出的接口
/* 分配一块内存,创建数据流 */ btStream* bts_create_strstream() ;
/* 从一个文件描述符创建数据流 */ btStream* bts_create_filestream_fp( FILE *fp, btsIo iodir) ;
/* 打开指定的文件创建数据流 */ btStream* bts_create_filestream( char *fname, btsIo iodir) ;
/* 销毁数据流 */ void bts_destroy( btStream* bts);
/* 将数据流的指针重置到起点 */ int bts_rewind( btStream* bts, btsIo iodir);
/* 返回数据流的缓冲区,只支持内存流。文件流调用了会返回{NULL, 0} */ struct btstrbuf bts_get_buf( btStream *bts) ;
/* 从数据流中读取len个字节到buf中,如果数据流中的数据不够len个字节,返回1 */ int bts_read( btStream* bts, char *buf, int len) ;
/* 向数据流写入长度为len的buf */ int bts_write( btStream* bts, char *buf, int len) ;
/* 这个函数看看源码就明白。说到底就是向流写入size*nmemb字节的数据 size_t writebts( void *buf, size_t size, size_t nmemb, void* stream ) { btStream *bts=stream; int len=size*nmemb; if (bts_write(bts, buf, len) != 0) { return 0; } return len; } 但是我有点觉得很不好的是,stream参数放在最后?和其它函数的风格都不同。 */ size_t writebts( void *buf, size_t size, size_t nmemb, void* stream );
/* 返回数据流当前指针位置的数据,但并不移动指针。 */ int bts_peek( btStream* bts) ;
/* 向数据流输入格式化字符串 */ int bts_printf( btStream* bts, char *fmt, ...) ;
/* * bts_scanbreak * Read from an input stream and copy characters into 'buf' until hitting * a break character. If there are any characters that are not listed * in 'cset' then an error value is returned. Scanbreak is used to read * bEncoded integers. * * bts - I/O stream, must be an input stream * cset - List of allowed characters * bset - List of break characters * buf - Return value of data read from the stream * bufsz - Available space in the buffer * * return 1 on error, 0 on success */ int bts_scanbreak( btStream* bts, char *cset, char *bset, char *buf, int bufsz);
types.c模块 -------------------------------------------------------------------------------- 这个模块的作用是提供保存bencode对象的容器和相关的操作函数。
照旧先看看相关结构
/* btObject只一个字段t标识容器类型,但实际上却不是容器(也就是说不保存任何数据)。*/ typedef enum btType { BT_STRING, BT_INTEGER, BT_LIST, BT_DICT } btType; typedef struct btObject { btType t; //容器的类型
/*... */ } btObject;
typedef struct btInteger { btObject parent; int allocated; //标识本结构分配哪(堆还是栈),以便destroy时处理
_int64 ival; } btInteger;
typedef struct btString { btObject parent; int allocated; int len; int bufsize; char *buf; } btString;
typedef struct btList { btObject parent; int allocated; int len; int listsize; btObject **list; } btList;
typedef struct btDict { btObject parent; int allocated; int len; int dictsize; btString *key; btObject **value; } btDict;
//
typedef struct btDictIt { btDict *d; int idx; } btDictIt;
下面再来看看模块的接口
/* Integer */ btInteger* btInteger_cast( btObject *o) ; btInteger* btInteger_create( btInteger *i); void btInteger_destroy( btInteger *i) ;
/* String */ btString* btString_cast( btObject *o) ; btString* btString_create( btString *s) ; btString* btString_create_str( btString *s, char *init) ; void btString_destroy( btString *s) ; char *btString_buf( btString *s) ; int btString_len( btString *s) ; int btString_cmp( btString *a, btString *b) ; void btString_setbuf( btString *s, char *buf, int len) ;
/* List */ btList* btList_cast( btObject *o) ; btList* btList_create( btList *buf) ; void btList_destroy( btList *buf) ; int btList_add( btList *_this, btObject *v) ; btObject* btList_index( btList *_this, int idx) ;
/* Dict */ btDict* btDict_cast( btObject *o) ; btDictIt* btDictIt_create( btDictIt *buf, btDict *d) ; btDictIt* btDict_iterator( btDict *d) ; int btDictIt_hasNext( btDictIt *i) ; btString* btDictIt_first( btDictIt *i) ; btString* btDictIt_next( btDictIt *i) ; btDict* btDict_create( btDict *buf) ; void btDict_destroy( btDict *_this) ; int btDict_add( btDict *_this, btString* k, btObject* v) ; btObject* btDict_find( btDict *_this, btString* k) ;
/* btObject */ int btObject_sizeof( btObject *b) ; int btObject_destroy( btObject *b) ; btObject* btObject_val( btObject *b, char *path); void btObject_dump( int depth, btObject *o);
无非就是转换函数,创建/销毁函数,还有各自的专用函数,对动态类型语言有所了解的话很易明白。 最后要说明一下的是 btObject* btObject_val( btObject *b, char *path); b一定要是list或者dict,path是以“/”分隔开的下标。比如有如果dict d= {'a': 1, 'c': [1, 2, 3, 4], 'b': 2} ------------------------------------- btObject* obj= btObject_val(b, 'c/2'); 返回的obj将会为BT_INTEGER类型,值为3
benc.c模块 -------------------------------------------------------------------------------- benc.c模块的作用就是组合bts.c和types.c模块。之后你就可以从数据流中读取bencode或者写入bencode * string */
/* all functions return 0 - success, 1 - error */ int benc_put_string( struct btStream* bts, btString *s) ; int benc_get_string( struct btStream* bts, btString *s) ;
/* integer */ int benc_put_int( struct btStream *bts, btInteger *i) ; int benc_get_int( struct btStream *bts, btInteger *i) ;
/* list */ int benc_put_list( struct btStream *bts, btList *a) ; int benc_get_list( struct btStream *bts, btList *a) ;
/* dictionary */ int benc_put_dict( struct btStream *bts, btDict *d) ; int benc_get_dict( struct btStream *bts, btDict *a) ;
/* object */ int benc_put_object( btStream *bts, btObject *o) ; int benc_get_object( btStream *bts, btObject **o) ;
所有函数都可以从函数名看出函数功能,我也不多说。
如果大家还有什么问题,可以向我提出。
下面给出调用这3个模块的函数的例子
#include "config.h"
#include <openssl/sha.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "bts.h" #include "types.h" #include "benc.h"
int readTorrent(char *filename) { int nRet = 0; btStream *bts = NULL; btObject *obj = NULL;
btInteger *btint = NULL;
//创见数据流
bts = bts_create_filestream(filename, BTS_INPUT); if (bts == NULL) return 1;
//从数据流读取bencode
nRet = benc_get_object(bts, &obj); if (nRet != 0) return 2; //输出.torrent文件的info/piece length
btint = BTINTEGER(btObject_val(obj, "info/piece length")); if (btint == 0) return 3; printf("\npiece length: %lld\n", btint->ival); //销毁
btObject_destroy(obj); bts_destroy(bts); return 0; }
int example1() { /*** 变量定义 ***/ char BTSTRING_TEST1[]= "test string1"; char BTSTRING_TEST2[]= "test string2"; char *p = NULL; int nRet = 0; btObject *btobj = NULL; btInteger *btint = NULL; btString *btstrA = NULL; btString *btstrB = NULL; btList *btlist = NULL; btDict *btdict = NULL;
btString *key = NULL; btString *val = NULL;
btDictIt *dictIt = NULL;
/*** btInteger ***/ printf("----------------------------------------------------------\n"); btint = btInteger_create(NULL); btint->ival = 1234567890; printf("btint: %lld\n", btint->ival);
/*** btString ***/ //btstrA
printf("----------------------------------------------------------\n"); btstrA = btString_create_str(NULL, BTSTRING_TEST1); printf("btstrA = %s\n", btString_buf(btstrA));
//btstrB
p = btmalloc(strlen(BTSTRING_TEST2) + 10); //buffer will be free by btString_destroy()
if (p == NULL) return 1; strcpy(p, BTSTRING_TEST2);
btstrB = btString_create(NULL); btString_setbuf(btstrB, p, strlen(p)); printf("btstrB = %s\n", btString_buf(btstrB)); //cmp
printf("btString_cmp = %d\n", btString_cmp(btstrA, btstrB));
/*** btList ***/ printf("----------------------------------------------------------\n"); btlist = btList_create(NULL); btList_add(btlist, BTOBJECT(btint)); btList_add(btlist, BTOBJECT(btstrA)); btList_add(btlist, BTOBJECT(btstrB));
btobj = btList_index(btlist, 0); btObject_dump(1, btobj); btobj = btList_index(btlist, 1); btObject_dump(1, btobj); btobj = btList_index(btlist, 2); btObject_dump(1, btobj);
/*** btDict ***/ printf("----------------------------------------------------------\n"); btdict = btDict_create(NULL); key = btString_create_str(NULL, "test key 1"); val = btString_create_str(NULL, "VALUE_of_dict"); btDict_add(btdict, key, BTOBJECT(val)); key = btString_create_str(NULL, "test key 2"); btDict_add(btdict, key, BTOBJECT(btlist));
btObject_dump(10, BTOBJECT(btdict));
/*** btDictIt ***/ printf("----------------------------------------------------------\n"); dictIt = btDict_iterator(btdict); //返回dict的迭代器
//输了所有key和value
if (btDictIt_hasNext(dictIt)) { key = btDictIt_first(dictIt); while (key != NULL) { printf("key>>>>>:"); btObject_dump(10, BTOBJECT(key)); //print key
btobj = btDict_find(btdict, key); printf("value>>>:"); btObject_dump(10, btobj); //print value
key = btDictIt_next(dictIt); } }
btfree(dictIt);
//最值得注意的是btList_add()和btDict_add()将输入的btObject拥有权据为己有,所以只要销毁dict即可。
btDict_destroy(btdict); // btList_destroy(btlist);
// btString_destroy(btstrB);
// btString_destroy(btstrA);
// btInteger_destroy(btint);
return 0; }
int main( int argc, char **argv) { int nRet = 0;
printf("%s\n", "hello");
nRet = readTorrent("/home/writer/119.torrent"); printf("readTorrent: %d\n", nRet);
nRet = example1(); printf("example1: %d\n", nRet);
return 0; }
对所有外围模块都有所了解以后,接下来我将对libbt的几个核心模块进行分析。
|