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;}