BT客户端开始一个下载首先要处理的就是torrent文件.
而torrent文件使用bencoding编码.
所以实现bencoding编码的解析器,就是第一步工作.
Bencoding is done as follows:
Strings are length-prefixed base ten followed by a colon and the string. For e
xample '4:spam' corresponds to 'spam'.
Integers are represented by an 'i' followed by the number in base 10 followed
by an 'e'. For example 'i3e' corresponds to 3 and 'i-3e' corresponds to -3. In
tegers have no size limitation. 'i-0e' is invalid. All encodings with a leadin
g zero, such as 'i03e', are invalid, other than 'i0e', which of course corresp
onds to 0.
Lists are encoded as an 'l' followed by their elements (also bencoded) followe
d by an 'e'. For example 'l4:spam4:eggse' corresponds to ['spam', 'eggs'].
Dictionaries are encoded as a 'd' followed by a list of alternating keys and t
heir corresponding values followed by an 'e'. For example, 'd3:cow3:moo4:spam4
:eggse' corresponds to {'cow': 'moo', 'spam': 'eggs'} and 'd4:spaml1:a1:bee' c
orresponds to {'spam': ['a', 'b']} . Keys must be strings and appear in sorted
order (sorted as raw strings, not alphanumerics).
下面是实现的bencoding解码器的VC++源代码:
//
/ /
#if !defined(AFX_BENCODE_H__4D0BB462_2AE0_45B3_8BE8_19D51B2DBB2E__INCLUDED_)
#define AFX_BENCODE_H__4D0BB462_2AE0_45B3_8BE8_19D51B2DBB2E__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#pragma warning( disable : 4786 )
#pragma warning( disable : 4355 )
#include
#include
#include
#include
using namespace std;
enum BEncodeParserErrorCode
{
enm_BEncodeErr_noerr = 0 , // 没有错误
enm_BEncodeErr_errString, // 错误的字符串
enm_BEncodeErr_errInt, // 错误的整型数据
enm_BEncodeErr_errList, // 错误的列表
enm_BEncodeErr_errDict, // 错误的词典结构
enm_BEncodeErr_End, // 文本结束
enm_BEncodeErr_unknown // 未知错误
};
enum BEncodeObjectType
{
enum_BEncodeType_Objectbase = 0 ,
enum_BEncodeType_String,
enum_BEncodeType_Int,
enum_BEncodeType_List,
enum_BEncodeType_Dict,
};
class CBEncodeObjectBase
{
public :
CBEncodeObjectBase(BEncodeObjectType type = enum_BEncodeType_Objectbase){m
_type = type;clear();}
virtual ~ CBEncodeObjectBase(){};
void clear(){szPos = NULL;m_error = enm_BEncodeErr_noerr;}
public :
BEncodeObjectType m_type; // 对象类型
char * szPos; // 对象在字符串中的位置
int ilen; // 对象的数据长度
BEncodeParserErrorCode m_error; // 错误值
};
class CBEncodeInt : public CBEncodeObjectBase
{
public :
CBEncodeInt() : CBEncodeObjectBase(enum_BEncodeType_Int) {}
virtual ~ CBEncodeInt(){}
public :
int m_iValue; // 整型对象的值
};
class CBEncodeString : public CBEncodeObjectBase
{
public :
CBEncodeString() : CBEncodeObjectBase(enum_BEncodeType_String) {m_szData =
NULL;}
virtual ~ CBEncodeString(){}
public :
bool getstring( string & strValue)
{
if (m_error == enm_BEncodeErr_noerr && m_szData)
{
strValue.assign(m_szData,m_ilen);
return true ;
}
return false ;
}
char * m_szData;
int m_ilen;
};
class CBEncodeList : public CBEncodeObjectBase
{
public :
CBEncodeList() : CBEncodeObjectBase(enum_BEncodeType_List) {}
virtual ~ CBEncodeList(){clear();}
void clear()
{
list::iterator it;
for (it = m_listObj.begin();it != m_listObj.end(); ++ it)
delete ( * it);
m_listObj.clear();
}
public :
list m_listObj;
};
class CBEncodeDict : public CBEncodeObjectBase
{
public :
CBEncodeDict() : CBEncodeObjectBase(enum_BEncodeType_Dict) {}
virtual ~ CBEncodeDict(){clear();}
CBEncodeObjectBase * getvalue( const char * szName)
{
map::iterator it = m_mapObj.find(szName);
if (it != m_mapObj.end())
return it -> second;
return NULL;
}
void clear()
{
list::iterator it;
for (it = m_listObj.begin();it != m_listObj.end(); ++ it)
delete ( * it);
m_listObj.clear();
m_mapObj.clear();
}
public :
map m_mapObj; //
list m_listObj; // 真正的对象保存在list中,list是一个nam
e对象一个value对象.map只是一个映射表,引用了指针而已
};
class CBEncode
{
public :
bool readint( char * szCurPos, int & iendpos,list & list
Obj);
bool readstring( char * szCurPos, int & iendpos,list & l
istObj);
bool readlist( char * szCurPos, int & iendpos,list & lis
tObj);
bool readdict( char * szCurPos, int & iendpos,list & lis
tObj);
bool parse( const char * szData);
CBEncode();
virtual ~ CBEncode();
void clear()
{
list::iterator it;
for (it = m_listObj.begin();it != m_listObj.end(); ++ it)
delete ( * it);
m_listObj.clear();
}
public :
list m_listObj;
CBEncodeObjectBase * m_plastObj; // 解析出来的最后一个对象
char * m_szTxt;
};
#endif // !defined(AFX_BENCODE_H__4D0BB462_2AE0_45B3_8BE8_19D51B2DBB2E__INCLUD
ED_)
// BEncode.cpp: implementation of the CBEncode class.
//
/ /
#include " stdafx.h "
#include " BEncode.h "
/ /
// Construction/Destruction
/ /
CBEncode::CBEncode()
{
m_plastObj = NULL;
m_szTxt = NULL;
}
CBEncode:: ~ CBEncode()
{
clear();
}
bool CBEncode::parse( const char * szData)
{
if (szData == NULL ||* szData == NULL)
return false ;
clear();
m_szTxt = ( char * )szData;
char * szCurPos = ( char * )szData;
int iendpos;
while ( * szCurPos)
{
if ( * szCurPos == ' i ' )
{
if ( ! readint(szCurPos,iendpos,m_listObj))
break ; // 遇到任何错误都终止整个解析
szCurPos += iendpos;
}
else if ( * szCurPos == ' l ' )
{
if ( ! readlist(szCurPos,iendpos,m_listObj))
break ;
szCurPos += iendpos;
}
else if ( * szCurPos == ' d ' )
{
if ( ! readdict(szCurPos,iendpos,m_listObj))
break ;
szCurPos += iendpos;
}
else
{
if ( ! readstring(szCurPos,iendpos,m_listObj))
break ;
szCurPos += iendpos;
}
}
if ( * szCurPos == 0 && m_plastObj -> m_error == enm_BEncodeErr_noerr)
return true ;
return false ;
}
// 从当前位置读取一个字符串
bool CBEncode::readstring( char * szCurPos, int & iendpos,list *> & listObj)
{
char * szTmp = szCurPos;
CBEncodeString * pNewString = new CBEncodeString;
pNewString -> szPos = szCurPos;
char szLen[ 20 ];
int i = 0 ;
while ( * szTmp >= ' 0 ' &&* szTmp <= ' 9 ' )
szLen[i ++ ] =* (szTmp ++ );
szLen = 0 ;
if ( * szTmp == ' : ' )
{
int ilen = atoi(szLen);
if (ilen > 0 )
{
pNewString -> m_szData = ++ szTmp;
pNewString -> m_ilen = ilen;
szTmp += ilen;
}
else
pNewString -> m_error = enm_BEncodeErr_errString;
}
else
pNewString -> m_error = enm_BEncodeErr_errString;
listObj.push_back(pNewString);
iendpos = szTmp - szCurPos;
m_plastObj = pNewString;
m_plastObj -> ilen = iendpos;
return pNewString -> m_error == enm_BEncodeErr_noerr ? true : false ;
}
// 读取一个整型数据
bool CBEncode::readint( char * szCurPos, int & iendpos,list
& listObj)
{
char * szTmp = szCurPos;
CBEncodeInt * pNewInt = new CBEncodeInt;
pNewInt -> szPos = szCurPos;
if ( * szTmp == ' i ' )
{
szTmp ++ ;
char szLen[ 20 ];
int i = 0 ;
while ( * szTmp >= ' 0 ' &&* szTmp <= ' 9 ' )
szLen[i ++ ] =* (szTmp ++ );
szLen = 0 ;
if ( * szTmp == ' e ' )
{
pNewInt -> m_iValue = atoi(szLen);
++ szTmp;
}
else
pNewInt -> m_error = enm_BEncodeErr_errInt;
}
else
pNewInt -> m_error = enm_BEncodeErr_errInt;
listObj.push_back(pNewInt);
iendpos = szTmp - szCurPos;
m_plastObj = pNewInt;
m_plastObj -> ilen = iendpos;
return pNewInt -> m_error == enm_BEncodeErr_noerr ? true : false ;
} // 读取一个列表
bool CBEncode::readlist( char * szCurPos, int & iendpos,list
& listObj)
{
char * szTmp = szCurPos;
CBEncodeList * pNewList = new CBEncodeList;
pNewList -> szPos = szCurPos;
if ( * szTmp == ' l ' )
{
szTmp ++ ;
int ilistendpos;
while ( * szTmp != ' e ' )
{
if ( * szTmp == ' i ' )
{
if ( ! readint(szTmp,ilistendpos,pNewList -> m_listObj))
break ; // 遇到任何错误都终止整个解析
szTmp += ilistendpos;
}
else if ( * szTmp == ' l ' )
{
if ( ! readlist(szTmp,ilistendpos,pNewList -> m_listObj))
break ;
szTmp += ilistendpos;
}
else if ( * szTmp == ' d ' )
{
if ( ! readdict(szTmp,ilistendpos,pNewList -> m_listObj))
break ;
szTmp += ilistendpos;
}
else
{
if ( ! readstring(szTmp,ilistendpos,pNewList -> m_listObj))
break ;
szTmp += ilistendpos;
}
}
if ( * szTmp != ' e ' || m_plastObj -> m_error != enm_BEncodeErr_noerr)
pNewList -> m_error = enm_BEncodeErr_errList;
else
szTmp ++ ;
}
else
pNewList -> m_error = enm_BEncodeErr_errList;
listObj.push_back(pNewList);
iendpos = szTmp - szCurPos;
m_plastObj = pNewList;
m_plastObj -> ilen = iendpos;
return pNewList -> m_error == enm_BEncodeErr_noerr ? true : false ;
}
// 读取一个字典
bool CBEncode::readdict( char * szCurPos, int & iendpos,list
& listObj)
{
char * szTmp = szCurPos;
CBEncodeDict * pNewDict = new CBEncodeDict;
pNewDict -> szPos = szCurPos;
if ( * szTmp == ' d ' )
{
szTmp ++ ;
int ilistendpos;
string strname;
while ( * szTmp != ' e ' )
{
if ( ! readstring(szTmp,ilistendpos,pNewDict -> m_listObj))
break ;
if (m_plastObj -> m_type != enum_BEncodeType_String)
break ;
strname.assign(((CBEncodeString * )m_plastObj) -> m_szData,((CBEncode
String * )m_plastObj) -> m_ilen);
szTmp += ilistendpos;
if ( * szTmp == ' i ' )
{
if ( ! readint(szTmp,ilistendpos,pNewDict -> m_listObj))
break ; // 遇到任何错误都终止整个解析
szTmp += ilistendpos;
}
else if ( * szTmp == ' l ' )
{
if ( ! readlist(szTmp,ilistendpos,pNewDict -> m_listObj))
break ;
szTmp += ilistendpos;
}
else if ( * szTmp == ' d ' )
{
if ( ! readdict(szTmp,ilistendpos,pNewDict -> m_listObj))
break ;
szTmp += ilistendpos;
}
else
{
if ( ! readstring(szTmp,ilistendpos,pNewDict -> m_listObj))
break ;
szTmp += ilistendpos;
}
pNewDict -> m_mapObj.insert(pair(strname
,m_plastObj));
}
if ( * szTmp != ' e ' || m_plastObj -> m_error != enm_BEncodeErr_noerr)
pNewDict -> m_error = enm_BEncodeErr_errDict;
else
szTmp ++ ;
}
else
pNewDict -> m_error = enm_BEncodeErr_errDict;
listObj.push_back(pNewDict);
iendpos = szTmp - szCurPos;
m_plastObj = pNewDict;
m_plastObj -> ilen = iendpos;
return pNewDict -> m_error == enm_BEncodeErr_noerr ? true : false ;
}