BitTorrent下载时,用户必须下载一个.torrent文件,它就是所谓"Metainfo file",里面存储有关于下载内容的announce地址,长度,大小,SHA1杂凑项等内容。它由Bencode编码组成.而且字符串是用UTF-8编码的。不过在中国,常常使用GBK编码。它由如下几项组成:
Info:描述下载内容的信息,是一个字典.有两种可能,一种是"单文件"模式:当BitTorrent只下载一个文件的时候使用.另一种是"多文件"模式,是在下载多个内容的时候使用.两种情况下Info各有不同.
单文件模式:
length:整数,指文件的大小.
md5sum:(可选),字符串,含有32字节md5校验码.BitTorrent没有使用MD5而是使用了SHA1作为自已的签名算法.这是为其他P2P软件兼容而设置的可选内容.
name:字符串,这是下载文件的名字,纯粹是建议.
piece length:整数,是BitTorrent文件块的大小.
pieces:字符串,连续的存放着所有块的SHA1杂凑值,每一个文件块的杂凑值为20字节.
多文件模式:
files: 一个由字典组成的列表,每个字典表示一个文件,字典的键值有如下内容:
length:整数,指当前文件的大小.
md5sum:(可选),字符串,同单文件模式,指当前文件.
path:由字符串组成的列表,每个列表元素指一个路径名中的一个目录或文件名.比如说:"l3:abc3:abc:6abc.txte",指文件路径"abc/abc/abc.txt".
name:字符串,BitTorrent下载路径中最上层的目录名
piece length:整数,是BitTorrent文件块的大小.
pieces:字符串,连续的存放着所有块的SHA1杂凑值,每一个文件块的杂凑值为20字节.
announce:字符串,指向tracker的URL.
announce-list:(可选),字典,这是一个对官方协议的扩展,支持"多Tracker".
creation date:(可选),整数,创建日期(UNIX创世纪格式:1970-1-1日00:00UTC到当时的秒数)
comment:(可选),字符串,注释
created by:可选,字符串,创建此.torrent文件的BT下载端程序名和版本号
encoding:BitComet对Metafile的扩展,一般用来指出不使用utf-8而使用gbk.
具体文件结构如下:
全部内容必须都为bencoding编码类型。
整个文件为一个字典结构,包含如下关键字
announce:tracker服务器的URL(字符串)
announce-list(可选):备用tracker服务器列表(列表)
creation date(可选):种子创建的时间,Unix标准时间格式,从1970 1月1日 00:00:00到创建时间的秒数(整数)
comment(可选):备注(字符串)
created by(可选):创建人或创建程序的信息(字符串)
info:一个字典结构,包含文件的主要信息,为分二种情况:单文件结构或多文件结构
单文件结构如下:
length:文件长度,单位字节(整数)
md5sum(可选):长32个字符的文件的MD5校验和,BT不使用这个值,只是为了兼容一些程序所保留!(字符串)
name:文件名(字符串)
piece length:每个块的大小,单位字节(整数)
pieces:每个块的20个字节的SHA1 Hash的值(二进制格式)
多文件结构如下:
files:一个字典结构
length:文件长度,单位字节(整数)
md5sum(可选):同单文件结构中相同
path:文件的路径和名字,是一个列表结构,如/test/test.txt 列表为l4:test8test.txte
程序如下:
class CBTCode
{
public:
CBTCode();
~CBTCode();
public:
BOOL BTEnCode(const BYTE* pData, int nLeftLen);
BOOL BTDeCode(BYTE* pData, int nLeftLen);
BTItemType GetType();
void SetType(BTItemType ItemType);
CBTBaseItem* GetValue();
void SetValue(CBTBaseItem* pBaseItem);
int GetBufSize();
private:
BOOL BTEnCode(const BYTE* pData, int nLeftLen, int& nLen);
BOOL BTDeCode(BYTE* pData, int nLeftLen, int& nLen);
private:
BTItemType m_ItemType;
union
{
CBTIntItem* m_pBTInt;
CBTStrItem* m_pBTStr;
CBTListItem* m_pBTList;
CBTDicItem* m_pBTDic;
};
};
CBTCode::CBTCode()
{
m_ItemType = BT_ITEM_UNKNOWN;
}
CBTCode::~CBTCode()
{
switch(m_ItemType)
{
case BT_ITEM_INT:
m_pBTInt->Release();
break;
case BT_ITEM_STR:
m_pBTStr->Release();
break;
case BT_ITEM_LIST:
m_pBTList->Release();
break;
case BT_ITEM_DIC:
m_pBTDic->Release();
break;
default:
break;
}
}
void CBTCode::SetType(BTItemType ItemType)
{
m_ItemType = ItemType;
}
BTItemType CBTCode::GetType()
{
return m_ItemType;
}
void CBTCode::SetValue(CBTBaseItem* pBaseItem)
{
switch(m_ItemType)
{
case BT_ITEM_INT:
m_pBTInt = (CBTIntItem*)pBaseItem;
m_pBTInt->AddRef();
break;
case BT_ITEM_STR:
m_pBTStr = (CBTStrItem*)pBaseItem;
m_pBTStr->AddRef();
break;
case BT_ITEM_LIST:
m_pBTList = (CBTListItem*)pBaseItem;
m_pBTList->AddRef();
break;
case BT_ITEM_DIC:
m_pBTDic = (CBTDicItem*)pBaseItem;
m_pBTDic->AddRef();
default:
break;
}
}
CBTBaseItem* CBTCode::GetValue()
{
switch(m_ItemType)
{
case BT_ITEM_INT:
return m_pBTInt;
case BT_ITEM_STR:
return m_pBTStr;
case BT_ITEM_LIST:
return m_pBTList;
case BT_ITEM_DIC:
return m_pBTDic;
default:
return NULL;
}
}
int CBTCode::GetBufSize()
{
switch(m_ItemType)
{
case BT_ITEM_INT:
return m_pBTInt->GetBufSize();
case BT_ITEM_STR:
return m_pBTStr->GetBufSize();
case BT_ITEM_LIST:
return m_pBTList->GetBufSize();
case BT_ITEM_DIC:
return m_pBTDic->GetBufSize();
default:
return 0;
}
}
//传内存的大小
BOOL CBTCode::BTEnCode(const BYTE* pData, int nLeftLen)
{
if (NULL == pData || *pData == 0 || nLeftLen <= 1)
return FALSE;
int nLen = 0;
const BYTE* pTemp = pData;
BOOL bRes = BTEnCode(pTemp, nLeftLen, nLen);
if (bRes && nLeftLen == nLen)
return TRUE;
return FALSE;
}
BOOL CBTCode::BTEnCode(const BYTE* pData, int nLeftLen, int& nLen)
{
const BYTE* pTemp = pData;
switch(*pTemp++)
{
case 'i':
{
char nVal[50] = {0};
int i = 0;
while(*pTemp >= '0' && *pTemp <= '9')
{
nVal[i++] = *pTemp++;
}
if (*pTemp == 'e')
{
m_ItemType = BT_ITEM_INT;
m_pBTInt = new CBTIntItem;
m_pBTInt->SetValue(_atoi64(nVal));
nLen = pTemp - pData + 1; //加1的原因是*pTemp = 'e'
return TRUE;
}
else
{
return FALSE;
}
}
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
{
pTemp--;
char nVal[50] = {0};
int i = 0;
while(*pTemp >= '0' && *pTemp <= '9')
{
nVal[i++] = *pTemp++;
}
int nStrLen = atoi(nVal);
if (*pTemp == ':')
{
pTemp++;
m_ItemType = BT_ITEM_STR;
m_pBTStr = new CBTStrItem;
m_pBTStr->SetValue(string((char*)pTemp, nStrLen));
pTemp = pTemp + nStrLen;
nLen = pTemp - pData; //字符串不是以e结束
return TRUE;
}
else
{
return FALSE;
}
}
case 'l':
{
m_ItemType = BT_ITEM_LIST;
m_pBTList = new CBTListItem;
int nTempLen = 0;
int nTempLeft = nLeftLen - 1;
while(*pTemp != 0 && *pTemp != 'e')
{
CBTCode btCode;
if (!btCode.BTEnCode(pTemp, nTempLeft, nTempLen))
return FALSE;
nTempLeft = nTempLeft - nTempLen;
pTemp = pTemp + nTempLen;
CBTBaseItem* pValue = btCode.GetValue();
m_pBTList->AddValue(pValue);
}
if (*pTemp == 'e')
{
nLen = pTemp - pData + 1;
return TRUE;
}
else
{
return FALSE;
}
}
case 'd':
{
m_ItemType = BT_ITEM_DIC;
m_pBTDic = new CBTDicItem;
int nTempLen =0;
int nTempLeft = nLeftLen - 1;
while(*pTemp != 0 && *pTemp != 'e')
{
CBTCode key;
if (!key.BTEnCode(pTemp, nTempLeft, nTempLen) || key.GetType() != BT_ITEM_STR)
return FALSE;
nTempLeft = nTempLeft - nTempLen;
pTemp = pTemp + nTempLen;
CBTCode value;
if (!value.BTEnCode(pTemp, nTempLeft, nTempLen))
return FALSE;
nTempLeft = nTempLeft - nTempLen;
pTemp = pTemp + nTempLen;
CBTStrItem* pKey = (CBTStrItem*)key.GetValue();
CBTBaseItem* pValue = value.GetValue();
m_pBTDic->AddValue(pKey->GetValue(), pValue);
}
if (*pTemp == 'e')
{
nLen = pTemp - pData + 1;
return TRUE;
}
else
{
return FALSE;
}
}
}
return FALSE;
}
//将数据结构内的值写到pData上
BOOL CBTCode::BTDeCode(BYTE* pData, int nLeftLen)
{
BYTE* pTemp = pData;
int nLen = 0;
return BTDeCode(pTemp, nLeftLen, nLen);
}
BOOL CBTCode::BTDeCode(BYTE* pData, int nLeftLen, int& nLen)
{
if (nLeftLen == 0)
return TRUE;
BYTE* pTemp = pData;
switch(m_ItemType)
{
case BT_ITEM_INT:
{
sprintf((char*)pTemp, "i%I64d", m_pBTInt->GetValue());
pTemp += strlen((char*)pTemp);
*pTemp++ = 'e';
nLen = pTemp - pData;
if (m_pBTInt->GetBufSize() != nLen)
return FALSE;
return TRUE;
}
case BT_ITEM_STR:
{
int strLen = m_pBTStr->GetValue().size();
sprintf((char*)pTemp, "%d:", strLen);
pTemp += Int2String(strLen).size() + 1;
memcpy(pTemp, m_pBTStr->GetValue().c_str(), strLen);
pTemp += strLen;
nLen = pTemp - pData;
if (m_pBTStr->GetBufSize() != nLen)
return FALSE;
return TRUE;
}
case BT_ITEM_LIST:
{
*pTemp++ = 'l';
nLeftLen--;
int AllLen = 0;
int nTempLen = 0;
for (int index = 0; index < m_pBTList->GetSize(); ++index)
{
nTempLen = 0;
CBTBaseItem* pBase = m_pBTList->GetValue(index);
CBTCode btCode;
btCode.SetType(pBase->GetItemType());
btCode.SetValue(pBase);
if (!btCode.BTDeCode(pTemp, nLeftLen - nTempLen, nTempLen))
return FALSE;
pTemp += nTempLen;
AllLen += nTempLen;
}
*pTemp++ = 'e';
nLeftLen--;
nLen = pTemp - pData;
if (m_pBTList->GetBufSize() != AllLen + 2)
return FALSE;
return TRUE;
}
case BT_ITEM_DIC:
{
*pTemp++ = 'd';
nLeftLen--;
int nTempLen = 0;
int nTempLeft = 0;
for (Map::iterator pos = m_pBTDic->m_MapValue.begin(); pos != m_pBTDic->m_MapValue.end(); ++pos)
{
nTempLeft = 0;
nTempLen = 0;
string strKey(pos->first);
int strLen = strKey.size();
sprintf((char*)pTemp, "%d:", strLen);
nTempLeft += Int2String(strLen).size() + 1;
pTemp += nTempLeft;
memcpy(pTemp, strKey.c_str(), strLen);
pTemp += strLen; //字符串个数
nTempLeft += strLen;
CBTBaseItem * pBase = pos->second;
CBTCode btCode;
btCode.SetType(pBase->GetItemType());
btCode.SetValue(pBase);
if (!btCode.BTDeCode(pTemp, nLeftLen - nTempLeft, nTempLen))
return FALSE;
pTemp += nTempLen;
}
*pTemp++ = 'e';
nLeftLen--;
nLen = pTemp - pData;
if (m_pBTDic->GetBufSize() != nLen)
return FALSE;
return TRUE;
}
default:
break;
}
return FALSE;
}