分类: LINUX
2011-03-16 13:59:54
一、Tracker HTTP协议
Tracker是一种HTTP/HTTPS服务,
它是专门为BitTorrent而设计的,和普通HTTP协议一样,采取请求和应答方式。BT客户端得到有关下载文件的各种动态信息,最主要的是下载同一
文件的其他下载者。.Tracker使用CGI方法提出申请,如"param1=value & param2 = val"
注意:所有不在0-9、a-z,、A-Z,和 $-_.+!*'()的字符集都要转义。比如空格要转义成"%20",其中20是空格的ASCII符。详看RFC1738.
客户端根据Metafile指出的"announce"地址,对tracker提出申请:如
...
下面是向tracker申请的参数:
info_hash: Info键值的20字节SHA杂凑值.
peer_id: 20字节长的独一无二的伙伴标识,在client启动时生成. 可用BT下载客户端的程序名和随机数来生成.
port: 客户端监听端口.典型的端口是6881-6889.
uploaded: 上传总量 (从前是客户端对tracker发送"开始"event) 十进制ASCII码数字. 它表示客户端上传字节总量.这一参数并没被官方描述提到.
downloaded: 下载总量 (从前是客户端对tracker发送"开始"event) 十进制ASCII码数字. 它表示客户端下载字节总量.这一参数并没被官方描述提到.
left: 剩余下载字节, 十进制ASCII码数字.
compact:
此参数值为1,表示期望得到紧凑模式的节点列表.
否则表示期望得到普通模式的节点列表.
指出客户端是否支持压缩模式. 如果是,伙伴列表将被一个伙伴字符串所代替.每个伙伴占6个字节.前4个字节是主机(网络字序) , 后2个字节是端口(网络字序).
event: 事件有3种: 开始(started), 完成(completed), 停止(stopped)
started: 对tracker的第一个请求必须包括开始事件.
stopped: 必须在客户端关闭时发送此事件.
completed: 必须在完成下载时发送此事件. 可是, 当在完成下载后重新开始任务就不可重发"完成"事件.
ip: 可选的IPV6地址
numwant: 可选的期望Tracker最大返回数.缺省为50个.
trackerid: 可选. 如果上次发布含有trackerid,这次就要重新扫送.
HTTP Tracker 回应消息
这是一个回应的样子
如果返回的bencode编码中包含failure reason字段,则表示处理请求失败,此字段的值即为失败原因.
如果请求成功,则有两个字段是必须出现的:
peers:节点列表
interval:服务器期望的下次查询间隔时间,单位为秒
通常还会有如下一些字段出现:
done peers:下载完毕的节点个数
num peers或者incomplete: 当前下载的节点个数
普通模式的回复其peers字段包括ip,port两个字段,如果未指定no_peer_id参数还将包括peer id字段.
Tracker交将返回一个"text/plain"文档,含有Bencode编码的字典:
failure reason: 如果有本项,说明发生了一个严重错误,将不会返回其他任何信息. 键值是人类可读的错误信息.
warning message: (新的) 键值是人类可读的的一般警告信息.
interval: 发送请求之间必须的间隔时间(秒) (必须执行)
min interval: 最小的发布间隔时间 (秒). 限制客户端重新发布.
tracker id: 一个必须被回送的字符串,当客户端再次发布.
complete: 整数, 拥有完全文件的伙伴数.
incomplete: 整数, 拥有不完全文件的伙伴数,也就是"水蛭".
peers: 一个含有字典的列表, 每一个字典含有如下内容:
peer id: 字符串, 伙伴的唯一名称.
ip: 字符串,伙伴的IPv4或IPv6地址,或是DNS名.
port: 整数,伙伴的端口
有一些Tracker能返回"Compact"模式的伙伴列表,如果是这种 情况,peers列表就会被一个peers字符串所代替,每个伙伴占6个字节.其中前4个字节是主机IP(网络字序) , 后2个字节是端口(网络字序).
BitComet对Tracke请求的扩展:
localip: 发送本地IP
hide: 隐身模式,不允许tracker返回你的IP给别人.
对返回的扩展:
tracker_alias_url:返回别名tracker的列表.如是同一个主机,主机名可以省略如udp://:8080/
二、Tracker UDP协议
先发送以下数据格式
Connecttion_ID(8位) 0(4位) Transaction_ID(4位)
收到以下数据格式
0(4位) Transaction_ID(4位) Connecttion_ID(8位)
然后再发送以下数据
InfoHash 20位
PeerID 20位
DownLoad 8位
Left 8位
UpLoad 8位
Event 4位
IP 4位
下面是8个字节的空(88-96位是空的内容)
port 4位
然后是可选内容
主要是服务器的HostName, UserName, PassWord
收到数据格式
1(4位) Transaction_ID(4位) Interval Time(4) 当前下载数目(4) 种子数(4)
然后是IP(4位)和Port(2位)
程序实现如下:
class CBTTracker
{
enum T_EVENT
{
EVENT_NONE,
EVENT_COMPLETED,
EVENT_STARTED,
EVENT_STOPPED
};
public:
CBTTracker();
~CBTTracker();
public:
int PreSelect(CBTFile* pCoreFile, fd_set* pReadFD, fd_set* pWriteFD, fd_set* pExceptFD);
int PosSelect(CBTFile* pCoreFile, fd_set* pReadFD, fd_set* pWriteFD, fd_set* pExceptFD);
void Close(CBTFile* pCoreFile);
private:
BOOL ParaseData(CBTFile* pCoreFile, const char* pData, int nSize); //针对HTTP TCP协议
private:
CBTSocket m_TrackSocket[MAX_TRACKER];
CBTTrackerURL m_TrackerURL;
CBTTrackerURL m_TempTrackers[MAX_TRACKER];
int m_CurTracker; //当前的数量
int m_State[MAX_TRACKER]; //状态
int m_CreateTime[MAX_TRACKER];
int m_nEvent;
int m_TransactionID;
int m_ConnectID;
int m_nInterval; //查询间隔时间
int m_CompletedFlag;
};
CBTTracker::CBTTracker()
{
m_CurTracker = -1;
memset(m_State, 0, MAX_TRACKER * sizeof(int));
memset(m_CreateTime, 0, MAX_TRACKER * sizeof(int));
m_nEvent = EVENT_STARTED;
m_TransactionID = 0;
m_ConnectID = 0;
m_CompletedFlag = 0;
}
CBTTracker::~CBTTracker()
{
for (int i = 0; i < MAX_TRACKER; ++i)
{
if (m_TrackSocket[i] > 0)
m_TrackSocket[i].Close();
}
}
int CBTTracker::PreSelect(CBTFile* pCoreFile, fd_set* pReadFD, fd_set* pWriteFD, fd_set* pExceptFD)
{
int nSize = pCoreFile->m_Trackers.size() - 1;
// nSize = 1;
if (m_CurTracker < nSize)
{
m_CurTracker++;
}
else
{
m_CurTracker = 0; //循环
}
BOOL bHttpPro = TRUE;
int thisTime = time(NULL);
switch(m_State[m_CurTracker])
{
case 0:
if (m_CreateTime[m_CurTracker] > thisTime)
return 0;
if (m_TempTrackers[m_CurTracker].Valid())
{
m_TrackerURL = m_TempTrackers[m_CurTracker];
m_TempTrackers[m_CurTracker].Clear();
}
else
{
m_TrackerURL.SetParase(pCoreFile->m_Trackers[m_CurTracker]);
}
if (!m_TrackerURL.Valid())
{
m_CompletedFlag++;
Close(pCoreFile);
return 0;
}
switch(m_TrackerURL.GetProtocol())
{
case CBTTrackerURL::BT_URL_HTTP:
{
m_CreateTime[m_CurTracker] = time(NULL) + 30;
if (m_TrackSocket[m_CurTracker] == INVALID_SOCKET)
{
if (!m_TrackSocket[m_CurTracker].Create(SOCK_STREAM, FALSE))
return 0;
}
string strAddr = CBTTCPSocket::GetHost(m_TrackerURL.GetHost().c_str());
if (strAddr.empty())
{
Close(pCoreFile);
return 0;
}
if (!m_TrackSocket[m_CurTracker].Connect(strAddr.c_str(), m_TrackerURL.GetPort()) &&
WSAGetLastError() != WSAEINPROGRESS &&
WSAGetLastError() != WSAEWOULDBLOCK)
{
LOG("Connect HTTP Failed %s : %d", strAddr.c_str(), m_TrackerURL.GetPort());
return 0;
}
LOG("Connect HTTP Succeed %s port = %d ", m_TrackerURL.GetHost().c_str(), m_TrackerURL.GetPort());
m_State[m_CurTracker] = 1;
}
break;
case CBTTrackerURL::BT_URL_UDP:
{
m_CreateTime[m_CurTracker] = time(NULL) + 30;
if (m_TrackSocket[m_CurTracker] == INVALID_SOCKET)
{
if (!m_TrackSocket[m_CurTracker].Create(SOCK_DGRAM, FALSE))
return 0;
}
string strAddr = CBTUDPSocket::GetHost(m_TrackerURL.GetHost().c_str());
if (strAddr.empty())
{
Close(pCoreFile);
return 0;
}
if (!m_TrackSocket[m_CurTracker].Connect(strAddr.c_str(), m_TrackerURL.GetPort()) &&
WSAGetLastError() != WSAEINPROGRESS &&
WSAGetLastError() != WSAEWOULDBLOCK)
{
return 0;
}
LOG("Connect UDP Succeed %s port = %d ", m_TrackerURL.GetHost().c_str(), m_TrackerURL.GetPort());
char SendData[16] = {0};
write_int(8, SendData, 0x41727101980);
write_int(4, SendData + 8, 0);
m_TransactionID = time(NULL);
write_int(4, SendData + 12, m_TransactionID);
if (m_TrackSocket[m_CurTracker].Send(SendData, 16) != 16)
return 0;
m_State[m_CurTracker] = 3;
LOG("UDP Send Succeeded");
}
break;
default:
return 0;
}
case 1:
{
FD_SET(m_TrackSocket[m_CurTracker], pWriteFD);
FD_SET(m_TrackSocket[m_CurTracker], pExceptFD);
}
case 2:
case 3:
case 4:
{
FD_SET(m_TrackSocket[m_CurTracker], pReadFD);
return m_TrackSocket[m_CurTracker];
}
}
return 0;
}
int CBTTracker::PosSelect(CBTFile* pCoreFile, fd_set* pReadFD, fd_set* pWriteFD, fd_set* pExceptFD)
{
switch(m_State[m_CurTracker])
{
case 1:
if (FD_ISSET(m_TrackSocket[m_CurTracker], pWriteFD)) //connect成功
{
stringstream os;
os << "GET " << m_TrackerURL.GetPath()
<< "?info_hash=" << uri_encode(pCoreFile->m_InfoHash)
<< "&peer_id=" << uri_encode(pCoreFile->m_PeerID)
<< "&port=" << pCoreFile->GetLocalPort()
<< "&downloaded=" << Int2String(pCoreFile->m_DownLoaded)
<< "&left=" << Int2String(pCoreFile->m_Left)
<< "&uploaded=" << Int2String(pCoreFile->m_UpLoaded)
<< "&compact=1"; //紧凑模式
/*if (f.local_ipa())
{
in_addr a;
a.s_addr = f.local_ipa();
os << "&ip=" << inet_ntoa(a);
}*/
switch (m_nEvent)
{
case EVENT_COMPLETED:
os << "&event=completed";
break;
case EVENT_STARTED:
os << "&event=started";
break;
case EVENT_STOPPED:
os << "&event=stopped";
break;
}
m_nEvent = EVENT_NONE;
os << " HTTP/1.0\r" << endl
<< "accept-encoding: gzip\r" << endl
<< "host: " << m_TrackerURL.GetHost();
if (m_TrackerURL.GetPort() != 80)
os << ':' << m_TrackerURL.GetPort();
os << '\r' << endl
<< '\r' << endl;
//向服务器发数据
if (m_TrackSocket[m_CurTracker].Send((void*)os.str().c_str(), os.str().size()) != os.str().size())
{
LOG("Send HTTP Traceker Failed");
Close(pCoreFile);
}
else
{
m_State[m_CurTracker] = 2;
LOG("Send HTTP traceker Succeeded");
}
}
else if (FD_ISSET(m_TrackSocket[m_CurTracker], pExceptFD))
{
LOG("HTTP WirteExept");
Close(pCoreFile);
}
break;
case 2:
if (FD_ISSET(m_TrackSocket[m_CurTracker], pReadFD))
{
char RecvData[16 * 1024] = {0};
int nRecTotalSize = 0;
for (int nRecSize = 0; nRecSize = m_TrackSocket[m_CurTracker].Receive(RecvData + nRecSize, 16 * 1024);)
{
if (nRecSize == SOCKET_ERROR)
{
int nError = WSAGetLastError();
if (nError != WSAEWOULDBLOCK)
{
LOG("Receive HTTP Failed! : %H", nError);
Close(pCoreFile);
}
return FALSE;
}
nRecTotalSize += nRecSize;
}
LOG("Receive HTTP Succeeded, nSize = %d", nRecTotalSize);
ParaseData(pCoreFile, RecvData, nRecTotalSize);
}
break;
case 3:
if (FD_ISSET(m_TrackSocket[m_CurTracker], pReadFD))
{
char RecvData[2048] = {0};
int ret = m_TrackSocket[m_CurTracker].Receive(RecvData, 2048);
if (ret != SOCKET_ERROR &&
ret >= 16 &&
read_int(4, RecvData + 4) == m_TransactionID &&
read_int(4, RecvData) == 0)
{
LOG("UDP Receive Succeeded");
m_ConnectID = read_int(8, RecvData + 8);
memset(RecvData, 0, 2048 * sizeof(char));
//包头
write_int(8, RecvData, m_TransactionID);
write_int(4, RecvData + 8, 0);
m_TransactionID = time(NULL);
write_int(4, RecvData + 12, m_TransactionID);
//内容
memcpy(RecvData + 16, pCoreFile->m_InfoHash.c_str(), 20);
memcpy(RecvData + 36, pCoreFile->m_PeerID.c_str(), 20);
write_int(8, RecvData + 56, pCoreFile->m_DownLoaded);
write_int(8, RecvData + 64, pCoreFile->m_Left);
write_int(8, RecvData + 72, pCoreFile->m_UpLoaded);
write_int(4, RecvData + 80, m_nEvent);
write_int(4, RecvData + 84, ::ntohl(pCoreFile->GetLocalIP()));
write_int(2, RecvData + 96, pCoreFile->GetLocalPort());
m_TrackSocket[m_CurTracker].Send(RecvData, 2048);
m_State[m_CurTracker] = 4;
}
}
break;
case 4:
if (FD_ISSET(m_TrackSocket[m_CurTracker], pReadFD))
{
char RecvData[2048] = {0};
int Ret = m_TrackSocket[m_CurTracker].Receive(RecvData, 2048);
if (Ret != SOCKET_ERROR &&
Ret >= 8 &&
read_int(4, RecvData + 4) == m_TransactionID &&
read_int(4, RecvData) == 1)
{
m_nInterval = read_int(4, RecvData + 8);
pCoreFile->m_nCurPeerNum = read_int(4, RecvData + 12);
pCoreFile->m_nSeedTotal = read_int(4, RecvData + 16);
for (int i = 20; i + 6 < Ret; i += 6)
{
int nIP = read_int(4, RecvData + i);
int nPort = read_int(2, RecvData + i + 4);
LOG("New Peer");
pCoreFile->InsertPeer(nIP, nPort);
}
}
else
{
}
}
break;
}
return 0;
}
void CBTTracker::Close(CBTFile* pCoreFile)
{
//m_TrackSocket.Close();
/* memset(m_State, 0, MAX_TRACKER * sizeof(int));
if (m_CompletedFlag == 0)
{
swap(pCoreFile->m_Trackers[0], pCoreFile->m_Trackers[m_CurTracker]);
m_CurTracker = -1;
}
else if (++m_CurTracker < pCoreFile->m_Trackers.size())
{
memset(m_CreateTime, 0, MAX_TRACKER * sizeof(int));
m_CompletedFlag--;
}
else */
{
// m_CurTracker = -1;
}
}
//紧凑模式和普通模式
BOOL CBTTracker::ParaseData(CBTFile* pCoreFile, const char* pData, int nSize)
{
for (int i = 0; i < nSize; ++i)
{
if (pData[i] == ' ') //找到第一个空格
{
int nResult = atoi(pData + i); //回应码一般只有3位
if (nResult == 302 || nResult == 301)
{
string strTemp = string(pData + i, nSize - i);
int a = strTemp.find("http://");
int b = strTemp.find_first_of(":", a + strlen("http://"));
if (string::npos == b)
{
m_TempTrackers[m_CurTracker].SetHost(strTemp.substr(a));
}
else
{
m_TempTrackers[m_CurTracker].SetHost(strTemp.substr(a + strlen("http://"), b - a - strlen("http//")));
if (strTemp[b] == '/')
{
m_TempTrackers[m_CurTracker].SetPath(strTemp.substr(b));
}
else
{
b++;
a = strTemp.find('/', b);
if (string::npos == a)
{
m_TempTrackers[m_CurTracker].SetPort(atoi(strTemp.substr(b).c_str()));
}
else
{
m_TempTrackers[m_CurTracker].SetPort(atoi(strTemp.substr(b, a - b).c_str()));
m_TempTrackers[m_CurTracker].SetPath(strTemp.substr(a, strlen("/announce")));
}
}
}
m_TempTrackers[m_CurTracker].SetProtocol(m_TrackerURL.GetProtocol());
return FALSE;
}
else if (nResult != 200)
{
return FALSE;
}
for (i = 0; i + 4 < nSize; ++i)
{
if (strncmp(pData + i, "\n\n", 2) == 0 || strncmp(pData + i, "\r\n\r\n", 4) == 0) //找到空格换行
{
const BYTE* pRes = NULL;
int nLen = 0;
if (pData[i] == '\n')
{
pRes = (BYTE*)pData + i + 2;
nLen = nSize - i - 2;
}
else
{
pRes = (BYTE*)pData + i + 4;
nLen = nSize - i - 4;
}
CBTCode TrackerRes;
if (!TrackerRes.BTEnCode(pRes, nLen))
{
return FALSE;
}
CBTDicItem* pBase = (CBTDicItem*)TrackerRes.GetValue();
if (pBase == NULL)
{
return FALSE;
}
CBTStrItem* pStr = (CBTStrItem*)pBase->GetValue("failure reason");
if (NULL == pStr)
{
CBTIntItem* pInt = (CBTIntItem*)pBase->GetValue("incomplete");
if (pInt)
pCoreFile->m_nCurPeerNum = pInt->GetValue();
pInt = (CBTIntItem*)pBase->GetValue("complete");
if (pInt)
pCoreFile->m_nSeedTotal = pInt->GetValue();
//下面解析Peers,有可能是字符串,也可能是列表结构
pStr = (CBTStrItem*)pBase->GetValue("peers");
if (pStr && !pStr->GetValue().empty())
{
string strPeers = pStr->GetValue();
const char* p = strPeers.c_str();
for (; p + 6 < strPeers.c_str() + strPeers.size(); p += 6)
{
int nIP = atoi(p);
int nPort = atoi(p + 4);
pCoreFile->InsertPeer(nPort, nPort);
}
}
else
{
CBTListItem* pPeerList = (CBTListItem*)pBase->GetValue("peers");
if (pPeerList)
{
for (int i = 0; i < pPeerList->GetSize(); ++i)
{
CBTDicItem* pDic = (CBTDicItem*)pPeerList->GetValue(i);
int nIP = inet_addr(((CBTStrItem*)pDic->GetValue("ip"))->GetValue().c_str());
int nPort = ((CBTIntItem*)pDic->GetValue("port"))->GetValue();
pCoreFile->InsertPeer(nIP, nPort);
}
}
}
}
return TRUE;
}
return FALSE;
}
return FALSE;
}
return FALSE;
}
return FALSE;
}