自学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();
};
/**//***************************************************************************************/
这个是实现:
/**/ /***************************************************************************************************************/
void StringType::assign( const std::vector < char > & str)
... {
clear();
m_str = str;
}
StringType::StringType( const std::vector < char > & str) : TypeBase(STRING_TYPE)
... {
assign(str);
}
StringType::StringType( const std:: string & str) : TypeBase(STRING_TYPE)
... {
std::vector<char> vstr(str.begin(), str.end());
assign(vstr);
}
StringType & StringType:: operator = ( const std::vector < char > & str)
... {
assign(str);
return *this;
}
StringType::StringType() : TypeBase(STRING_TYPE), m_str( 0 ) ... {}
StringType::StringType( const StringType & other) : TypeBase(STRING_TYPE) ... { assign(other.m_str); }
StringType & StringType:: operator = ( const StringType & other)
... {
if(this != &other)
assign(other.m_str);
return *this;
}
/**/ /*****************************************************************************************************/
IntegerType::IntegerType() : TypeBase(INTEGER_TYPE), m_int( 0 ) ... {}
IntegerType & IntegerType:: operator = ( const t_u_int64 n) ... { m_int = n; return *this; }
IntegerType::IntegerType( const IntegerType & other) : TypeBase(INTEGER_TYPE)
... {
m_int = other.m_int;
}
IntegerType::IntegerType(t_u_int64 n) : TypeBase(INTEGER_TYPE), m_int(n) ... {}
IntegerType & IntegerType:: operator = ( const IntegerType & other)
... {
if(this != &other)
m_int = other.m_int;
return *this;
}
/**/ /*****************************************************************************************************/
ListType::ListType() : TypeBase(LIST_TYPE) ... {}
ListType:: ~ ListType() ... { clear(); }
void ListType::clear()
... {
ThisIterator beg = m_list_data.begin();
ThisIterator end = m_list_data.end();
for(ThisIterator it = beg; it != end; ++it)
delete (*it);
m_list_data.clear();
}
void ListType::insert(TypeBase * ptype)
... {
m_list_data.push_back(ptype);
}
/**/ /*****************************************************************************************************/
TypeBase * DictType::get_value( const std:: string & key)
... {
C_Iterator beg = m_map_data.begin();
C_Iterator end = m_map_data.end();
for(C_Iterator it = beg; it != end; ++it)...{
if((it->first->get_str()) == key)...{
return (it->second);
}
}
return NULL;
}
bool DictType::set_value(StringType * pstr_bt, TypeBase * ptype_bt)
... {
m_map_data[pstr_bt] = ptype_bt;
return true;
}
void DictType::clear()
... {
ThisIterator beg = m_map_data.begin();
ThisIterator end = m_map_data.end();
for(ThisIterator it = beg; it != end; ++it)...{
delete (it->first);
delete (it->second);
}
}
四种类型都派生自一个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()...{};
} ;
#define ASSERT_POS(pos) { /*assert(pos < bfile.size())*/ if(pos >= bfile.size()) return 0;}
#define ERR_CHECK(num) { /*assert(num != 0);*/if(num == 0) return 0; }
bool BExtractor::set_content( const std::vector < t_byte > & meta)
... {
std::vector<t_byte>::const_iterator it;
it = std::find(meta.begin(), meta.end(), 'd');
if(it == meta.end()) return false;
bfile.clear();
bfile.resize(meta.size());
std::copy(it, meta.end(), bfile.begin());
return true;
}
size_t BExtractor::get_content(size_t b, size_t len, std::vector < t_byte > & content)
... {
ASSERT_POS(b);
ASSERT_POS(b + len);
content.resize(len);
std::vector<t_byte>::const_iterator beg = bfile.begin() + b;
std::vector<t_byte>::const_iterator end = beg + len;
std::copy(beg, end, content.begin());
return len;
}
size_t BExtractor::analyzer(size_t beg_pos, TypeBase *& pbase)
... {
IntegerType *pint = NULL;
StringType *pstr = NULL;
ListType *plist = NULL;
DictType *pdict = NULL;
ASSERT_POS(beg_pos);
size_t idx = beg_pos;
char ch = bfile[idx];
switch(ch)...{
case 'i':
pint = new IntegerType();
pbase = pint;
return read_int(idx, *pint);
case 'l':
plist = new ListType();
pbase = plist;
return read_list(idx, *plist);
case 'd':
pdict = new DictType();
pbase = pdict;
return read_dict(idx, *pdict);
default:
pstr = new StringType();
pbase = pstr;
return read_string(idx, *pstr);
}
}
size_t BExtractor::read_list(size_t beg_pos, ListType & list_bt)
... {
TypeBase *pbase = NULL;
size_t idx = beg_pos + 1; //跳过'l';
ASSERT_POS(idx);
while(bfile[idx] != 'e')...{
size_t i = 0;
i = analyzer(idx, pbase);
ERR_CHECK(i);
idx += i;
ASSERT_POS(idx);
list_bt.insert(pbase);
}
return (idx + 1 - beg_pos);
}
size_t BExtractor::read_dict(size_t beg_pos, DictType & dict_bt)
... {
size_t idx = beg_pos + 1; //跳过'd';
ASSERT_POS(idx);
TypeBase *pbase = NULL;
StringType *pstr = NULL;
while(bfile[idx] != 'e')...{
pstr = new StringType();
size_t i = 0;
i = read_string(idx, *pstr);
ERR_CHECK(i);
idx += i;
ASSERT_POS(idx);
i = analyzer(idx, pbase);
ERR_CHECK(i);
idx += i;
ASSERT_POS(idx);
dict_bt.set_value(pstr, pbase);
}
return (idx + 1 - beg_pos);
}
size_t BExtractor::read_string(size_t beg_pos, StringType & str_bt)
... {
str_bt.clear();
std::string num; //得到前面的数值 4:abcd的4
size_t idx = beg_pos;
ASSERT_POS(idx);
while(bfile[idx] != ':')...{
num += bfile[idx];
++idx;
ASSERT_POS(idx);
}
++idx; //跳过':';
size_t len = atoi(num.c_str());
ASSERT_POS((idx + len));
std::vector<char> str(len);
for(size_t i = 0; i < len; ++i)
str[i] = bfile[idx + i];
if(str.size() != len) return 0;
str_bt = str;
return (idx + len - beg_pos);
}
size_t BExtractor::read_int(size_t beg_pos, IntegerType & int_bt)
... {
size_t idx = beg_pos + 1; //跳过'i'
ASSERT_POS(idx);
if(bfile[idx] == 'e') return 0; //如果中间无数值数据
std::string num;
while(bfile[idx] != 'e')...{
char ch = bfile[idx];
if(!std::isdigit(ch) && ch != '-') return 0;
num += ch;
++idx;
ASSERT_POS(idx);
}
int_bt = atoi(num.c_str());
return (idx - beg_pos + 1);
}
size_t BExtractor::locate_key(size_t beg_pos, const std:: string & key)
... {
assert(bfile[beg_pos] == 'd');
size_t idx = beg_pos + 1; //跳过'd';
ASSERT_POS(idx);
TypeBase *pbase = NULL;
StringType key_bt;
while(bfile[idx] != 'e')...{
size_t i = 0;
i = read_string(idx, key_bt);
ERR_CHECK(i);
idx += i;
ASSERT_POS(idx);
if(key_bt.get_str() == key)
return idx;
i = analyzer(idx, pbase);
if(i == 0 || ((idx + i) >= bfile.size()))...{
delete pbase;
return 0;
}
idx += i;
}
delete pbase;
return 0;
}
#undef ASSERT_POS
#undef ERR_CHECK
这个类负责抽取Bencoding文件为一个巨型的DictType,当然,也可以根据需要再合适的位置进行相应的操作,例如后期的tracker request时候解码tcp tracker返回的信息,这是后话,暂且不提.大概知道是干什么的就成了.下面是对生成的这个DICT类型的信息提取,使用函数
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转换等繁杂的辅助类,相互的耦合度的比较大, 这个相对来说还是最独立的,所以有些源码就不贴了,有需要的朋友请加我QQ.
Bencoding解码先说到这,明天有空将贴tracker查寻的源码,我尽量保证每天写一篇笔记以记录自己的工作进度!