自学C++一年有余,一直想自己开发个什么东西,终于在今年7月份决定写个BT的客户端吧,恩,说干就干,于是开始了这次痛苦的旅程,越深入到各个细节当中越觉得自己可能完不成这个东西了,故决定开始写Blog激励自己一下,也希望能把一个编程新手遇到的各种困难都记录下来,供他人借鉴,也便于自己日后的查寻. 我这人比较罗嗦,可能整个文档中间会夹杂一些个人感悟,期望哪位不幸读到的朋友谅解. 有对P2P一类软件感兴趣的朋友可以加我qq: 2070341一起聊聊, 自己写东西实在是很孤独.
说干就干,说写就写,先从种子文件开始.
(1): Bencoding格式
这个东西是所有相关资源的来源,所以必须先搞定. 下面简单阐述一下,大概格式,有兴趣的网友可以到http://wiki.theory.org/BitTorrentSpecification 参照标准.
Bencoding采用的是一种字典格式,其中包括四种数据类型,string, int, list和dictionary.其中list和dict都是容器,能包含这四种类型的任意一种,dict的key当然就是string,注意,学过设计模式的朋友想到什么了? 对Composite!
string 4:abcd
int i1234e
list l4:abcdi1234ee 一个装有一个string和一个int的list
dict d3:aaal4:abcdi1234eee 一个装有一条记录的字典,key为aaa,value是个list
下面是一段解析bencoding程序的源码,因为整个bencoding文件就是一个dict,所以最后生成的是个dict,然后再用具体的类来解析这个dict得到相应的内容,请看:


/**//***************************************************************************************/


enum DataType ...{ STRING_TYPE, INTEGER_TYPE, LIST_TYPE, DICT_TYPE };

class TypeBase
...{
private:
DataType obj_type;
public:
TypeBase(const DataType &data_type) : obj_type(data_type) ...{}
virtual ~TypeBase() ...{}
public:
DataType type() const ...{ return obj_type; }
virtual void clear() = 0;
};
class StringType : public TypeBase
...{
private:
//std::string m_str;
std::vector<char> m_str;
public:
StringType();
StringType(const StringType& other);
StringType(const std::vector<char>& str);
StringType(const std::string &str);
~StringType()...{}
StringType& operator= (const StringType &other);
StringType& operator= (const std::vector<char> &str);
public:
void assign(const std::vector<char> &str);
size_t size() const ...{ return m_str.size(); }
std::string get_str() const ...{ return std::string(m_str.begin(), m_str.end()); }
void get_str(std::vector<char> &str) ...{ str = m_str; }
public:
virtual void clear() ...{ m_str.clear(); };
};
class IntegerType : public TypeBase
...{
private:
t_u_int64 m_int;
public:
IntegerType();
IntegerType& operator=(const t_u_int64 n);
IntegerType(const IntegerType& other);
IntegerType(const t_u_int64 n);
IntegerType& operator=(const IntegerType &other);
~IntegerType()...{}
public:
t_u_int64 get_int()const ...{ return m_int; }
virtual void clear() ...{}
};


class ListType : public TypeBase
...{
public:
typedef std::list<TypeBase*>::iterator ThisIterator;
typedef std::list<TypeBase*>::const_iterator C_Iterator;
private:
std::list<TypeBase*> m_list_data;
private:
ListType(const ListType& other);
ListType& operator= (const ListType & other);
public:
ListType();
~ListType();
public:
void insert(TypeBase *ptype); 
size_t size() ...{ return m_list_data.size(); }
ThisIterator begin() ...{ return m_list_data.begin(); }
ThisIterator end() ...{ return m_list_data.end(); }
C_Iterator const_begin() const ...{ return m_list_data.begin(); }
C_Iterator const_end() const ...{ return m_list_data.end(); }
virtual void clear();
};


class DictType : public TypeBase
...{
public:
typedef std::map<StringType*, TypeBase*>::iterator ThisIterator;
typedef std::map<StringType*, TypeBase*>::const_iterator C_Iterator;
private:
std::map<StringType*, TypeBase*> m_map_data;
private:
DictType(const DictType&);
DictType& operator=(const DictType&);
public:
DictType() : TypeBase(DICT_TYPE) ...{}
~DictType() ...{ clear(); }
public:
TypeBase* get_value( const std::string& key);
bool set_value(StringType* pstr_bt, TypeBase* ptype_bt);
size_t size() ...{ return m_map_data.size(); }
C_Iterator const_begin() const ...{ return m_map_data.begin(); }
C_Iterator const_end() const ...{ return m_map_data.end(); }
ThisIterator begin() ...{ return m_map_data.begin(); }
ThisIterator end() ...{ return m_map_data.end(); }
public:
virtual void clear();
};

/**//***************************************************************************************/
四种类型都派生自一个TypeBase基类, 故ListType和DictType只需装入一个TypeBase*就OK了,Runtime识别的时候用基类的type()const函数返回的DataType 来识别,我这里并没有给基类提供一个更宽大的接口,没必要.
下面是解码类 BExtractor

class BExtractor : private NonCopyable
...{
private:
private:
std::vector<t_byte> bfile;
public:
size_t analyzer(size_t beg_pos, TypeBase *&pbase);//供给read_list和read_dict递归提取类型
size_t read_string(size_t beg_pos, StringType &str_bt);
//从指定位置开始位置(基于0)
//将内容加入到一个ListType中,内部调用analyzer函数, 会导致递归调用
//任何错误都将导致返回 0,直接解析失败
//如无错误,则返回读取的内容长度,
size_t read_int(size_t beg_pos, IntegerType &int_bt);
size_t read_list(size_t beg_pos, ListType &list_bt);
size_t read_dict(size_t beg_pos, DictType &dict_bt);
//根据执行起始位置(此起始位置
size_t locate_key(size_t beg_pos, const std::string &key);
public:
//接受一段元数据作分析 当然是bencoding file, 如果未搜索到第一个d,则返回false;
bool set_content(const std::vector<t_byte> &meta);
size_t get_content(size_t b, size_t e, std::vector<t_byte> &content); //根据执行位置提取一段元数据
void clear() ...{ bfile.clear(); }
public:
BExtractor()...{};
~BExtractor()...{};
};
这个类负责抽取Bencoding文件为一个巨型的DictType,当然,也可以根据需要再合适的位置进行相应的操作,例如后期的tracker request时候解码tcp tracker返回的信息,这是后话,暂且不提.大概知道是干什么的就成了.下面是对生成的这个DICT类型的信息提取,使用函数
bool seed_decoder(const std::string &fname, SeedInfo &seed_info);
SeedInfo结构为:

struct FileInfo
...{
t_u_int64 length;
std::string md5sum;
std::list<std::string> path;
public:
FileInfo();
~FileInfo() ...{}
FileInfo(const FileInfo& other);
FileInfo& operator=(const FileInfo &other);
public:
void clear();
};
struct SeedInfo
...{
bool is_multi_files;
std::vector<t_u_int8> info_hash;
//std::string announce; //tracker服务器的URL(字符串)
std::list<std::string> anounce_list; //备用tracker服务器列表(列表)
t_u_int64 creation_date;
std::string comment; //备注(字符串)
std::string created_by; //创建人或创建程序的信息(字符串)
std::string name; //多文件时使用,name:最上层的目录名字(字符串)
t_u_int64 piece_lengh; //每个块的大小,单位字节(整数)
std::vector<char> pieces; //每个块的20个字节的SHA1 Hash的值(二进制格式)
std::string publisher;
std::string publisher_url;
std::list<FileInfo> files; //多文件时使用;
size_t pieces_num;
//utf8扩展
bool is_utf8;
std::string publisher_url_utf8;
std::string publisher_utf8;
std::string name_utf8;
std::string comment_utf8;
private:
void copy(const SeedInfo &other);
public:
SeedInfo();
SeedInfo(const SeedInfo& other);
SeedInfo& operator= (const SeedInfo &other);
~SeedInfo() ...{}
void clear();
};
这个函数的内部实现相当混乱,我不作过分的叙述了,日后要改的,这里有必要提一下的是 : std::vector<t_u_int8> info_hash; //这个数据成员,
它是由整个Bencoding格式文件的字典中的"Info" 这个key所对应的dict类型的160位sha1 hash值, 这个有必要说一下,不太清楚的请参阅相关资料,这东西最好从bit的角度理解,故此,info_hash这个数据成员的.size()为20, typedef unsigned char t_u_int8;
这个几个类都被我放到了头文件bencoding_type.h头文件中了.由于本人软件的设计能力有限,所以整个架构比较乱,后面写了一个包括网络,异步I/O,完成端口,code page转换等庞大的lib库,相互的耦合度的比较大, 这个相对来说还是最独立的,所以源码一概不贴,有需要的朋友请加我QQ.
Bencoding解码先说到这,明天有空将贴tracker查寻的源码,我尽量保证每天写一篇笔记以记录自己的工作进度!