织梦CMS - 轻松建站从此开始!

罗索

P2P Tracker HTTP协议

jackyhwei 发布于 2011-04-27 09:42 点击:次 
Tracker是一种HTTP/HTTPS服务, 它是专门为BitTorrent而设计的,和普通HTTP协议一样,采取请求和应答方式。BT客户端得到有关下载文件的各种动态信息,最主要的是下载同一 文件的其他下载者。.Tracker使用CGI方法提出申请,如"param1=value & param2 = val"
TAG:

一、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提出申请:如
http://tk.greedland.net/announce?info_hash=XXXXXXXXXXXXXXXXXXXX&peer_id=AZ34343&port=6881...

下面是向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)   //找到空格换行 (poseidonqiu)

本站文章除注明转载外,均为本站原创或编译欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,同学习共成长。转载请注明:文章转载自:罗索实验室 [http://www.rosoo.net/a/201104/11317.html]
本文出处:blog.chinaunix.net 作者:poseidonqiu
顶一下
(1)
100%
踩一下
(0)
0%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片
栏目列表
将本文分享到微信
织梦二维码生成器
推荐内容