|
与基于udp协议的tracker服务器进行交互(1)
通常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 <map>#include <set>#include <vector>#include <queue>#include <string>using namespace std;#endifclass CPeerHostInfo{public:DWordIP;//节点IPWORDPort;//节点端口};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,intievent,DWORD dwIP,WORDwPort);BOOL Disconnect();public:SOCKETm_socket;DWORDm_dwIP;WORDm_wPort;__int64m_iConnection_id;DWORDm_dwConnectTick;stringm_strError;//如果请求失败,此变量保存错误信息DWORD m_dwDonePeers;//种子数DWORD m_dwNumPeers;//当前下载者个数DWORD m_dwInterval;//查询间隔时间list<CPeerHostInfo> 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_FILEstatic char THIS_FILE[]=__FILE__;#define new DEBUG_NEW#endif//////////////////////////////////////////////////////////////////////// ConstrUCtion/Destruction//////////////////////////////////////////////////////////////////////#define RECVBUFSIZE 2048CUDPTrackerClient::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);}elsestrHost = strServer;}else{strHost = strServer;m_wPort = wPort;}if(m_wPort == 0)m_wPort = 80;//Check if address is an IP or a Domain Nameint 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);elsem_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_INfrom;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字节的数据缓冲区指针//pPeerID20字节的数据缓冲区指针//ievent参数值://none = 0 //completed = 1 //started = 2 //stopped = 3 DWORD CUDPTrackerClient::Announcing(BYTE* pInfoHash,BYTE * pPeerID,__int64 idownloaded,__int64 ileft,__int64 iuploaded,intievent,DWORD dwIP,WORDwPort){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_INfrom;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;}
|