Tracker HTTP协议和Tracker UDP协议

    技术2022-05-11  51

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

    注意:所有不在0-9、a-z,、A-Z,和 $-_.+!*'()的字符集都要转义。比如空格要转义成" ",其中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)   //找到空格换行    {     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;}

     

     


    最新回复(0)