一个人的战争 (1) : Bencoding格式解码

        自学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类型的信息提取,使用函数


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;     
           //种子创建的时间,Unix标准时间格式,从1970 1月1日 00:00:00到创建时间的秒数(整数)
        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查寻的源码,我尽量保证每天写一篇笔记以记录自己的工作进度!

  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值