BitTorrent 协议规范(BT协议集合)四

通常BT客户端每几分钟就要向tracker发送一次请求.对于一些比较大的BT站点,其tracker的压力是可想而知的.降低tracker的压力首先考虑到的当然是采用更低网络开销的udp协议.于是Bittorrent udp-tracker protocol应运而生.
  这个协议很简单.
  下面是实现它的封装类: 

 

//  UDPTrackerClient.h: interface for the CUDPTrackerClient class. 
//  
/

#if  !defined(AFX_UDPTRACKERCLIENT_H__69B6ACC8_8193_4680_81D8_925B1550E92C__INCLUDED_) 
#define  AFX_UDPTRACKERCLIENT_H__69B6ACC8_8193_4680_81D8_925B1550E92C__INCLUDED_ 

#if  _MSC_VER > 1000 
#pragma  once 
#endif   //  _MSC_VER > 1000 

#include <WINSOCK2.H> 
#pragma  comment(lib, "ws2_32.lib") 

#ifndef _DISABLEWARNING4786_4355 
#define  _DISABLEWARNING4786_4355 
#pragma  warning( disable : 4786 ) 
#pragma  warning( disable : 4355 ) 
#endif  
#ifndef _ENABLEUSESTL 
#define  _ENABLEUSESTL 
#include <LIST> 
#include <SET> 
#include <VECTOR> 
#include <QUEUE> 
#include <STRING> 
#include <MAP> 
using   namespace  std; 
#endif  

class  CPeerHostInfo 

public :     
DWORD IP;
// 节点IP 
WORD Port; // 节点端口 
}; 
class  CUDPTrackerClient 

public
CUDPTrackerClient(); 
virtual   ~ CUDPTrackerClient(); 
void  CancelSocketOperate(); 
BOOL Connect(
const   char   *  szServer,WORD wPort  =   0 ); 
DWORD Announcing(BYTE
*  pInfoHash,BYTE  *  pPeerID, 
__int64 idownloaded,__int64 ileft,__int64 iuploaded, 
int  ievent, 
DWORD dwIP,WORD wPort); 

BOOL Disconnect(); 
public
SOCKET   m_socket; 
DWORD   m_dwIP; 
WORD   m_wPort; 
__int64   m_iConnection_id; 
DWORD   m_dwConnectTick; 
string    m_strError;  // 如果请求失败,此变量保存错误信息 
DWORD m_dwDonePeers;  // 种子数 
DWORD m_dwNumPeers;  // 当前下载者个数 
DWORD m_dwInterval;  // 查询间隔时间 
list m_listPeers; 
}; 

#endif   //  !defined(AFX_UDPTRACKERCLIENT_H__69B6ACC8_8193_4680_81D8_925B1550E92C__INCLUDED_) 
//  UDPTrackerClient.cpp: implementation of the CUDPTrackerClient class. 
//  
/

#include 
" stdafx.h "  
#include 
" UDPTrackerClient.h "  

#include 
" DataStream.h "  
#ifdef _DEBUG 
#undef  THIS_FILE 
static   char  THIS_FILE[] = __FILE__; 
#define  new DEBUG_NEW 
#endif  

/
//  Construction/Destruction 
/
#define  RECVBUFSIZE 2048 

CUDPTrackerClient::CUDPTrackerClient() 

m_socket 
=  INVALID_SOCKET; 
m_iConnection_id 
=   0
m_dwConnectTick 
=   0
m_dwIP 
=   0
m_wPort 
=   0
m_dwDonePeers 
=   0 // 种子数 
m_dwNumPeers  =   0 // 当前下载者个数 
m_dwInterval  =   0 // 查询间隔时间 


CUDPTrackerClient::
~ CUDPTrackerClient() 

Disconnect(); 


void  CUDPTrackerClient::CancelSocketOperate() 

if (m_socket  !=  INVALID_SOCKET) 

LINGER lingerStruct;    
//  If we're supposed to abort the connection, set the linger value 
//  on the socket to 0.    
lingerStruct.l_onoff  =   1
lingerStruct.l_linger 
=   0
setsockopt(m_socket, SOL_SOCKET, SO_LINGER, 
  (
char   * ) & lingerStruct,  sizeof (lingerStruct) ); 


BOOL CUDPTrackerClient::Disconnect() 

m_iConnection_id 
=   0
m_dwDonePeers 
=   0 // 种子数 
m_dwNumPeers  =   0 // 当前下载者个数 
m_dwInterval  =   0 // 查询间隔时间 
if  ( m_socket  !=  INVALID_SOCKET ) 

m_dwIP 
=   0
m_wPort 
=   0
//  Now close the socket handle. This will do an abortive or 
//  graceful close, as requested.     
shutdown(m_socket,SD_BOTH); 
closesocket(m_socket); 
m_socket 
=  INVALID_SOCKET; 
return  TRUE; 

return  FALSE; 


// szServer连接的主机,可以是下列形式的字符串: 
// easeso.com:1000 
// easeso.com 
// 如果wPort不为0,则szServer不应该包含端口信息 
BOOL CUDPTrackerClient::Connect( const   char   *  szServer,WORD wPort) 

m_strError 
=   ""
BOOL bRes 
=  FALSE; 
if  ( m_socket  ==  INVALID_SOCKET ) 

// 用UDP初始化套接字 
BOOL optval  =  TRUE;    
m_socket 
= socket(AF_INET,SOCK_DGRAM, 0 ); 
if (m_socket  ==  INVALID_SOCKET) 
  
return  FALSE; 
// 设置超时时间 
int  TimeOut = 10000
int  err  =  setsockopt (m_socket, SOL_SOCKET,SO_RCVTIMEO,(CHAR  * & TimeOut, sizeof  (TimeOut)); 

if (m_dwIP  ==   0

CString strServer 
=  szServer; 
CString strHost; 
if (wPort  ==   0

  
int  iNext  =  strServer.Find( ' : ' ); 
  
if (iNext > 0
  {    
  strHost 
=  strServer.Mid( 0 ,iNext);    
  CString strPort 
=  strServer.Mid(iNext + 1 );    
  m_wPort 
=  (WORD)atoi(strPort); 
  } 
  
else  
  strHost 
=  strServer; 

else  

  strHost 
=  strServer; 
  m_wPort 
=  wPort; 

if (m_wPort  ==   0
  m_wPort 
=   80

// Check if address is an IP or a Domain Name 
int  a  =  strHost[ 0 ]; 
if  (a  >   47   &&  a  <   58
  m_dwIP 
=  inet_addr(strHost); 
else  

  
struct  hostent  * pHost; 
  pHost 
=  gethostbyname(strHost); 
  
if (pHost  !=  NULL) 
  m_dwIP 
=   * ((ULONG * )pHost -> h_addr); 
  
else  
  m_dwIP 
=   0


if ((GetTickCount() - m_dwConnectTick) > 30000

m_dwConnectTick 
=   0
m_iConnection_id 
=   0

if (m_socket  !=  INVALID_SOCKET  &&  m_dwIP  &&  m_wPort  &&  m_iConnection_id  == 0

DWORD dwTransaction_id 
=  GetTickCount(); 
SOCKADDR_IN from; 
int  fromlength = sizeof (SOCKADDR); 

char  buf[RECVBUFSIZE]; 
from.sin_family
= AF_INET; 
from.sin_addr.s_addr
= m_dwIP; 
from.sin_port
= htons(m_wPort); 
CDataStream sendstream(buf,
2047 ); 
sendstream.clear(); 
__int64 iCID 
=   0x41727101980
sendstream.writeint64(CNetworkByteOrder::convert(iCID)); 
sendstream.writedword(CNetworkByteOrder::convert((
int ) 0 )); 
sendstream.writedword(dwTransaction_id); 

int  iRes  =   0
int  iTimes  =   6
while (iTimes > 0 && m_dwIP) 

  sendto(m_socket,sendstream.getbuffer(),sendstream.size(),
0 ,( struct  sockaddr FAR  * ) & from, sizeof (from)); 
  iRes 
=  recvfrom(m_socket,buf,RECVBUFSIZE - 1 , 0 ,( struct  sockaddr FAR  * ) & from,( int  FAR  * ) & fromlength); 
  
if (iRes  >= 0
  
break
  iTimes
--

if (iRes >= 16

  CDataStream recvstream(buf,RECVBUFSIZE
- 1 ); 
  DWORD dwAction 
=  (DWORD)CNetworkByteOrder::convert(( int )recvstream.readdword()); 
  DWORD dwTIDResp
=  recvstream.readdword(); 
  
if (dwTIDResp  ==  dwTransaction_id) 
  { 
  
if (dwAction  ==   0
  { 
  m_iConnection_id 
=  recvstream.readint64();    
  
// BitComet将回复0x16字节数据,最后6字节是服务器查看到的本地IP和UDP端口 
  } 
  
else   if (dwAction  ==   3 ) // 得到一个错误信息包 
  { 
  buf[iRes]
= 0
  m_strError 
=  recvstream.readstring(); 
  } 
  } 


if (m_iConnection_id) 
bRes 
=  TRUE; 
return  bRes; 

// 提交请求 
// pInfoHash 20字节的数据缓冲区指针 
// pPeerID 20字节的数据缓冲区指针 
// ievent参数值: 
// none = 0 
// completed = 1 
// started = 2 
// stopped = 3 

DWORD CUDPTrackerClient::Announcing(BYTE
*  pInfoHash,BYTE  *  pPeerID, 
      __int64 idownloaded,__int64 ileft,__int64 iuploaded, 
      
int  ievent, 
      DWORD dwIP,WORD wPort) 

m_listPeers.clear(); 
m_dwNumPeers 
=   0
m_dwDonePeers 
=   0
m_strError 
=   ""
DWORD dwReturnCode 
=   0
if (m_iConnection_id  &&  m_socket  !=  INVALID_SOCKET  &&  m_dwIP  &  m_wPort) 

DWORD dwTransaction_id 
=  GetTickCount(); 
// srand(dwTransaction_id); 
// DWORD dwKey = rand(); 
DWORD dwKey  =   0x3753
SOCKADDR_IN from; 
int  fromlength = sizeof (SOCKADDR); 
char  buf[RECVBUFSIZE]; 
from.sin_family
= AF_INET; 
from.sin_addr.s_addr
= m_dwIP; 
from.sin_port
= htons(m_wPort); 
CDataStream sendstream(buf,RECVBUFSIZE
- 1 ); 
sendstream.clear(); 
sendstream.writeint64(m_iConnection_id); 
sendstream.writedword(CNetworkByteOrder::convert((
int ) 1 )); 
sendstream.writedword(dwTransaction_id); 
sendstream.writedata(pInfoHash,
20 ); 
sendstream.writedata(pPeerID,
20 ); 
sendstream.writeint64(CNetworkByteOrder::convert(idownloaded)); 
sendstream.writeint64(CNetworkByteOrder::convert(ileft)); 
sendstream.writeint64(CNetworkByteOrder::convert(iuploaded)); 
sendstream.writedword(CNetworkByteOrder::convert(ievent)); 
sendstream.writedword(dwIP); 
sendstream.writedword(CNetworkByteOrder::convert((
int )dwKey)); 
sendstream.writedword(CNetworkByteOrder::convert((
int ) 200 )); 
sendstream.writedword(CNetworkByteOrder::convert(wPort)); 

int  iRes  =   0
int  iTimes  =   2
while (iTimes > 0 && m_dwIP) 

  sendto(m_socket,sendstream.getbuffer(),sendstream.size(),
0 ,( struct  sockaddr FAR  * ) & from, sizeof (from)); 
  iRes 
=  recvfrom(m_socket,buf,RECVBUFSIZE - 1 , 0 ,( struct  sockaddr FAR  * ) & from,( int  FAR  * ) & fromlength); 
  
if (iRes  >= 0
  
break
  iTimes
--

if (iRes >= 20
{    
  CDataStream recvstream(buf,RECVBUFSIZE
- 1 ); 
  DWORD dwAction 
=  (DWORD)CNetworkByteOrder::convert(( int )recvstream.readdword()); 
  DWORD dwTIDResp
=  recvstream.readdword(); 
  
if (dwTIDResp  ==  dwTransaction_id) 
  { 
  
if (dwAction  ==   1
  { 
  m_dwInterval 
=  (DWORD)CNetworkByteOrder::convert(( int )recvstream.readdword()); 
  m_dwNumPeers 
=  (DWORD)CNetworkByteOrder::convert(( int )recvstream.readdword()); 
  m_dwDonePeers 
=  (DWORD)CNetworkByteOrder::convert(( int )recvstream.readdword());    
  CPeerHostInfo hi;    
  
for ( int  iCurPos  =   20 ;iCurPos + 6 <= iRes;iCurPos += 6
  { 
    hi.IP
=  recvstream.readdword(); 
    hi.Port 
=  (WORD)CNetworkByteOrder::convert((unsigned  short )recvstream.readword()); 
    m_listPeers.push_back(hi); 
  } 
  
if (m_dwNumPeers > m_listPeers.size()) 
  { 
    iRes 
=   0
    iTimes 
=   6
    
while (iTimes > 0 && m_dwIP) 
    { 
    iRes 
=  recvfrom(m_socket,buf,RECVBUFSIZE - 1 , 0 ,( struct  sockaddr FAR  * ) & from,( int  FAR  * ) & fromlength); 
    
if (iRes  >= 0
    
break
    iTimes
--
    } 
    
if (iRes >= 6
    { 
    
for (iCurPos  =   0 ;iCurPos + 6 <= iRes;iCurPos += 6
    { 
    hi.IP
=  recvstream.readdword(); 
    hi.Port 
=  (DWORD)CNetworkByteOrder::convert(( int )recvstream.readword()); 
    m_listPeers.push_back(hi); 
    } 
    }     
  } 
  m_dwNumPeers 
=  m_listPeers.size(); 
  dwReturnCode 
=   200
  } 
  
else   if (dwAction  ==   3 ) // 得到一个错误信息包 
  { 
  buf[iRes]
= 0
  m_strError 
=  recvstream.readstring(); 
  dwReturnCode 
=   400
  } 
  } 


// 每次都要求重新连接 
m_iConnection_id  =   0
return  dwReturnCode; 

//  DataStream.h: interface for the CDataStream class. 
//  
/

#if  !defined(AFX_DATASTREAM_H__D90A2534_EA73_4BEA_8B7E_87E59A3D1D26__INCLUDED_) 
#define  AFX_DATASTREAM_H__D90A2534_EA73_4BEA_8B7E_87E59A3D1D26__INCLUDED_ 

#if  _MSC_VER > 1000 
#pragma  once 
#endif   //  _MSC_VER > 1000 
#include 
// 数据流操作函数 
class  CDataStream 

public  : 
CDataStream(
char   *  szBuf, int  isize) 

m_isize 
=  isize; 
buffer 
=  szBuf; 
current 
=  buffer; 

~ CDataStream() 


  
void  clear() 

current 
=  buffer; 
current[
0 ] = 0

// 此函数不动态增加内存,一次打印的数据长度不应该超过缓冲区的三分之一,否则可能导致失败 
bool  printf( const   char   *  format,...) 

if (current) 

  
if (current  -  buffer  >  (m_isize * 2 ) / 3
  
return   false
  va_list argPtr ; 
  va_start( argPtr, format ) ; 
  
int  count  =  vsprintf( current, format, argPtr ) ; 
  va_end( argPtr ); 
  current 
+=  count ; 
  
return   true

return   false

// 此函数拷贝字符串 
bool  strcpy( const   char   *  szStr) 

if (current && szStr) 

  
int  ilen  =  lstrlen(szStr); 
  
if ((m_isize - (current  -  buffer))  <  (ilen  + 2 )) 
  
return   false
  memcpy(current,szStr,ilen
+ 1 ); 
  current 
+=  ilen; 
  
return   true

return   false

char   *  getcurrentpos() 

return  current; 

void  move( int  ilen) // 当前指针向后移动ilen 

current 
+=  ilen; 

void  reset() 

current 
=  buffer; 

BYTE readbyte() 

current 
++
return   * (current - 1 ); 

void  writebyte(BYTE btValue) 

* current  =  btValue; 
current 
++

WORD readword() 

current 
+= 2
return   * ((WORD * )(current - 2 )); 

void  writeword(WORD wValue) 

* ((WORD * )current)  =  wValue; 
current 
+= 2

DWORD readdword() 

current 
+= 4
return   * ((DWORD * )(current - 4 )); 

void  writedword(DWORD dwValue) 

* ((DWORD * )current)  =  dwValue; 
current 
+= 4

__int64 readint64() 

current 
+= 8
return   * ((__int64 * )(current - 8 )); 

void  writeint64(__int64 iValue) 

* ((__int64 * )current)  =  iValue; 
current 
+= 8

BYTE 
*  readdata(DWORD dwLen) 

current 
+= dwLen; 
return  (BYTE * )(current - dwLen); 

void  writedata(BYTE  *  pData,DWORD dwLen) 

memcpy(current,pData,dwLen); 
current 
+= dwLen; 

char   *  readstring() 

char   *  szRes  =  current; 
int  ilen  =  lstrlen(current); 
current 
+= (ilen + 1 ); 
return  szRes; 

int  size() 

return  ( int )(current - buffer); 

const   char   *  getbuffer(){ return  buffer;} 
private  : 
char *  buffer; 
  
char *  current; 
int  m_isize; 
}; 
class  CNetworkByteOrder 

public
static  unsigned  short   int  convert(unsigned  short   int  iValue) 

unsigned 
short   int  iData; 
((BYTE
* ) & iData)[ 0 =  ((BYTE * ) & iValue)[ 1 ]; 
((BYTE
* ) & iData)[ 1 =  ((BYTE * ) & iValue)[ 0 ]; 
return  iData; 

static   int  convert( int  iValue) 

int  iData; 
((BYTE
* ) & iData)[ 0 =  ((BYTE * ) & iValue)[ 3 ]; 
((BYTE
* ) & iData)[ 1 =  ((BYTE * ) & iValue)[ 2 ]; 
((BYTE
* ) & iData)[ 2 =  ((BYTE * ) & iValue)[ 1 ]; 
((BYTE
* ) & iData)[ 3 =  ((BYTE * ) & iValue)[ 0 ]; 
return  iData; 

static  __int64 convert(__int64 iValue) 

__int64 iData; 
((BYTE
* ) & iData)[ 0 =  ((BYTE * ) & iValue)[ 7 ]; 
((BYTE
* ) & iData)[ 1 =  ((BYTE * ) & iValue)[ 6 ]; 
((BYTE
* ) & iData)[ 2 =  ((BYTE * ) & iValue)[ 5 ]; 
((BYTE
* ) & iData)[ 3 =  ((BYTE * ) & iValue)[ 4 ]; 
((BYTE
* ) & iData)[ 4 =  ((BYTE * ) & iValue)[ 3 ]; 
((BYTE
* ) & iData)[ 5 =  ((BYTE * ) & iValue)[ 2 ]; 
((BYTE
* ) & iData)[ 6 =  ((BYTE * ) & iValue)[ 1 ]; 
((BYTE
* ) & iData)[ 7 =  ((BYTE * ) & iValue)[ 0 ]; 
return  iData; 


}; 
#endif   //  !defined(AFX_DATASTREAM_H__D90A2534_EA73_4BEA_8B7E_87E59A3D1D26__INCLUDED_)


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值