📄 downloadtransferbt.cpp
字号:
SetState( dtsTorrent );
theApp.Message( MSG_DEBUG, _T("Download from %s was choked."), (LPCTSTR)m_sAddress );
for ( CFileFragment* pFragment = m_pRequested ; pFragment != NULL ; pFragment = pFragment->m_pNext )
{
CBTPacket* pPacket = CBTPacket::New( BT_PACKET_CANCEL );
pPacket->WriteLongBE( (DWORD)( pFragment->m_nOffset / m_pDownload->m_pTorrent.m_nBlockSize ) );
pPacket->WriteLongBE( (DWORD)( pFragment->m_nOffset % m_pDownload->m_pTorrent.m_nBlockSize ) );
pPacket->WriteLongBE( (DWORD)pFragment->m_nLength );
Send( pPacket );
}
m_pRequested->DeleteChain();
m_pRequested = NULL;
m_nRequested = 0;
return TRUE;
}
BOOL CDownloadTransferBT::OnUnchoked(CBTPacket* pPacket)
{
m_bChoked = FALSE;
SetState( dtsTorrent );
m_pRequested->DeleteChain();
m_pRequested = NULL;
m_nRequested = 0;
theApp.Message( MSG_DEBUG, _T("Download from %s was UNchoked."), (LPCTSTR)m_sAddress );
return SendRequests();
}
//////////////////////////////////////////////////////////////////////
// CDownloadTransferBT request pipe
BOOL CDownloadTransferBT::SendRequests()
{
ASSERT( m_nState == dtsTorrent || m_nState == dtsRequesting || m_nState == dtsDownloading );
if ( m_bChoked || ! m_bInterested )
{
if ( m_nRequested == 0 ) SetState( dtsTorrent );
return TRUE;
}
if ( m_nRequested >= (int)Settings.BitTorrent.RequestPipe )
{
if ( m_nState != dtsDownloading ) SetState( dtsRequesting );
return TRUE;
}
QWORD nBlockSize = m_pDownload->m_pTorrent.m_nBlockSize;
ASSERT( nBlockSize != 0 );
if ( nBlockSize == 0 ) return TRUE;
CFileFragment* pPossible = m_pDownload->GetFirstEmptyFragment()->CreateCopy();
if ( ! m_pDownload->m_bTorrentEndgame )
{
for ( CDownloadTransfer* pTransfer = m_pDownload->GetFirstTransfer() ; pTransfer && pPossible ; pTransfer = pTransfer->m_pDlNext )
{
pTransfer->SubtractRequested( &pPossible );
}
}
while ( m_nRequested < (int)Settings.BitTorrent.RequestPipe )
{
QWORD nOffset, nLength;
if ( SelectFragment( pPossible, &nOffset, &nLength ) )
{
ChunkifyRequest( &nOffset, &nLength, Settings.BitTorrent.RequestSize, FALSE );
CFileFragment::Subtract( &pPossible, nOffset, nLength );
CFileFragment* pRequest = CFileFragment::New( NULL, m_pRequested, nOffset, nLength );
if ( m_pRequested != NULL ) m_pRequested->m_pPrevious = pRequest;
m_pRequested = pRequest;
m_nRequested ++;
int nType = ( m_nDownloaded == 0 || ( nOffset % nBlockSize ) == 0 )
? MSG_DEFAULT : MSG_DEBUG;
theApp.Message( nType, IDS_DOWNLOAD_FRAGMENT_REQUEST,
nOffset, nOffset + nLength - 1,
(LPCTSTR)m_pDownload->GetDisplayName(), (LPCTSTR)m_sAddress );
#ifdef _DEBUG
DWORD ndBlock1 = (DWORD)( nOffset / nBlockSize );
DWORD ndBlock2 = (DWORD)( ( nOffset + nLength - 1 ) / nBlockSize );
ASSERT( ndBlock1 < m_pDownload->m_pTorrent.m_nBlockCount );
ASSERT( ndBlock1 == ndBlock2 );
ASSERT( nLength <= nBlockSize );
#endif
CBTPacket* pPacket = CBTPacket::New( BT_PACKET_REQUEST );
pPacket->WriteLongBE( (DWORD)( nOffset / nBlockSize ) );
pPacket->WriteLongBE( (DWORD)( nOffset % nBlockSize ) );
pPacket->WriteLongBE( (DWORD)nLength );
Send( pPacket );
}
else
{
break;
}
}
if ( pPossible == NULL && m_pDownload->m_bTorrentEndgame == FALSE )
{
m_pDownload->m_bTorrentEndgame = Settings.BitTorrent.Endgame;
}
pPossible->DeleteChain();
if ( m_nRequested > 0 && m_nState != dtsDownloading ) SetState( dtsRequesting );
if ( m_nRequested == 0 ) SetState( dtsTorrent );
return TRUE;
}
//////////////////////////////////////////////////////////////////////
// CDownloadTransferBT fragment selection
BOOL CDownloadTransferBT::SelectFragment(CFileFragment* pPossible, QWORD* pnOffset, QWORD* pnLength)
{
ASSERT( pnOffset != NULL && pnLength != NULL );
if ( pPossible == NULL ) return FALSE;
QWORD nBlockSize = m_pDownload->m_pTorrent.m_nBlockSize;
CFileFragment* pComplete = NULL;
DWORD nBlock;
ASSERT( nBlockSize != 0 );
for ( ; pPossible ; pPossible = pPossible->m_pNext )
{
if ( pPossible->m_nOffset % nBlockSize )
{
// the start of a block is complete, but part is missing
nBlock = (DWORD)( pPossible->m_nOffset / nBlockSize );
ASSERT( nBlock < m_pDownload->m_pTorrent.m_nBlockCount );
if ( m_pAvailable == NULL || m_pAvailable[ nBlock ] )
{
*pnOffset = pPossible->m_nOffset;
*pnLength = nBlockSize * (QWORD)nBlock + nBlockSize - *pnOffset;
*pnLength = min( *pnLength, pPossible->m_nLength );
ASSERT( *pnLength <= nBlockSize );
pComplete->DeleteChain();
return TRUE;
}
}
else if ( ( pPossible->m_nLength % nBlockSize ) &&
( pPossible->m_nOffset + pPossible->m_nLength < m_pDownload->m_nSize ) )
{
// the end of a block is complete, but part is missing
nBlock = (DWORD)( ( pPossible->m_nOffset + pPossible->m_nLength ) / nBlockSize );
ASSERT( nBlock < m_pDownload->m_pTorrent.m_nBlockCount );
if ( m_pAvailable == NULL || m_pAvailable[ nBlock ] )
{
*pnOffset = nBlockSize * (QWORD)nBlock;
*pnLength = pPossible->m_nOffset + pPossible->m_nLength - *pnOffset;
ASSERT( *pnLength <= nBlockSize );
pComplete->DeleteChain();
return TRUE;
}
}
else
{
// this fragment contains one or more aligned empty blocks
nBlock = (DWORD)( pPossible->m_nOffset / nBlockSize );
*pnLength = pPossible->m_nLength;
ASSERT( *pnLength != 0 );
for ( ; ; nBlock ++, *pnLength -= nBlockSize )
{
ASSERT( nBlock < m_pDownload->m_pTorrent.m_nBlockCount );
if ( m_pAvailable == NULL || m_pAvailable[ nBlock ] )
{
pComplete = CFileFragment::New( NULL, pComplete, (QWORD)nBlock, 0 );
}
if ( *pnLength <= nBlockSize ) break;
}
}
}
if ( CFileFragment* pRandom = pComplete->GetRandom() )
{
*pnOffset = pRandom->m_nOffset * nBlockSize;
*pnLength = nBlockSize;
*pnLength = min( *pnLength, m_pDownload->m_nSize - *pnOffset );
ASSERT( *pnLength <= nBlockSize );
pComplete->DeleteChain();
return TRUE;
}
else
{
ASSERT( pComplete == NULL );
return FALSE;
}
}
//////////////////////////////////////////////////////////////////////
// CDownloadTransferBT multi-source fragment handling
BOOL CDownloadTransferBT::SubtractRequested(CFileFragment** ppFragments)
{
if ( m_nRequested == 0 || m_bChoked ) return FALSE;
CFileFragment::Subtract( ppFragments, m_pRequested );
return TRUE;
}
BOOL CDownloadTransferBT::UnrequestRange(QWORD nOffset, QWORD nLength)
{
if ( m_nRequested == 0 ) return FALSE;
ASSERT( m_pDownload->m_pTorrent.m_nBlockSize != 0 );
if ( m_pDownload->m_pTorrent.m_nBlockSize == 0 ) return FALSE;
CFileFragment** ppPrevious = &m_pRequested;
BOOL bMatch = FALSE;
for ( CFileFragment* pFragment = *ppPrevious ; pFragment ; )
{
CFileFragment* pNext = pFragment->m_pNext;
if ( nOffset < pFragment->m_nOffset + pFragment->m_nLength &&
nOffset + nLength > pFragment->m_nOffset )
{
CBTPacket* pPacket = CBTPacket::New( BT_PACKET_CANCEL );
pPacket->WriteLongBE( (DWORD)( pFragment->m_nOffset / m_pDownload->m_pTorrent.m_nBlockSize ) );
pPacket->WriteLongBE( (DWORD)( pFragment->m_nOffset % m_pDownload->m_pTorrent.m_nBlockSize ) );
pPacket->WriteLongBE( (DWORD)pFragment->m_nLength );
Send( pPacket );
*ppPrevious = pNext;
if ( pNext ) pNext->m_pPrevious = pFragment->m_pPrevious;
pFragment->DeleteThis();
m_nRequested --;
bMatch = TRUE;
}
else
{
ppPrevious = &pFragment->m_pNext;
}
pFragment = pNext;
}
return bMatch;
}
//////////////////////////////////////////////////////////////////////
// CDownloadTransferBT piece reception
BOOL CDownloadTransferBT::OnPiece(CBTPacket* pPacket)
{
ASSERT( m_pClient != NULL );
if ( pPacket->GetRemaining() < 8 ) return TRUE;
if ( m_nState != dtsRequesting && m_nState != dtsDownloading ) return TRUE;
SetState( dtsDownloading );
DWORD nBlock = pPacket->ReadLongBE();
QWORD nOffset = pPacket->ReadLongBE();
QWORD nLength = pPacket->GetRemaining();
nOffset += (QWORD)nBlock * m_pDownload->m_pTorrent.m_nBlockSize;
m_nDownloaded += nLength;
m_pDownload->m_nTorrentDownloaded += nLength;
m_pSource->AddFragment( nOffset, nLength );
m_pSource->SetValid();
CFileFragment::Subtract( &m_pRequested, nOffset, nLength );
m_nRequested = m_pRequested->GetCount();
m_pDownload->SubmitData( nOffset,
pPacket->m_pBuffer + pPacket->m_nPosition, nLength );
// TODO: SendRequests and ShowInterest could be combined.. SendRequests
// is probably going to tell us if we are interested or not
ShowInterest();
return SendRequests();
}
//////////////////////////////////////////////////////////////////////
// CDownloadTransferBT source exchange
BOOL CDownloadTransferBT::OnSourceResponse(CBTPacket* pPacket)
{
CBuffer pInput;
pInput.Add( pPacket->m_pBuffer, pPacket->GetRemaining() );
CBENode* pRoot = CBENode::Decode( &pInput );
if ( pRoot == NULL ) return TRUE;
CBENode* pPeers = pRoot->GetNode( "peers" );
if ( ! pPeers->IsType( CBENode::beList ) )
{
delete pRoot;
return TRUE;
}
int nCount = 0;
for ( int nPeer = 0 ; nPeer < pPeers->GetCount() ; nPeer++ )
{
CBENode* pPeer = pPeers->GetNode( nPeer );
if ( ! pPeer->IsType( CBENode::beDict ) ) continue;
CBENode* pURL = pPeer->GetNode( "url" );
if ( pURL->IsType( CBENode::beString ) )
{
nCount += m_pDownload->AddSourceURL( pURL->GetString(), TRUE );
}
else
{
CBENode* pID = pPeer->GetNode( "peer id" );
if ( ! pID->IsType( CBENode::beString ) || pID->m_nValue != sizeof(SHA1) ) continue;
CBENode* pIP = pPeer->GetNode( "ip" );
if ( ! pIP->IsType( CBENode::beString ) ) continue;
CBENode* pPort = pPeer->GetNode( "port" );
if ( ! pPort->IsType( CBENode::beInt ) ) continue;
SOCKADDR_IN saPeer;
if ( ! Network.Resolve( pIP->GetString(), (int)pPort->GetInt(), &saPeer ) ) continue;
theApp.Message( MSG_DEBUG, _T("CDownloadTransferBT::OnSourceResponse(): %s: %s:%i"),
(LPCTSTR)m_sAddress,
(LPCTSTR)CString( inet_ntoa( saPeer.sin_addr ) ), htons( saPeer.sin_port ) );
nCount += m_pDownload->AddSourceBT( (SHA1*)pID->m_pValue,
&saPeer.sin_addr, htons( saPeer.sin_port ) );
}
}
delete pRoot;
theApp.Message( MSG_DEFAULT, IDS_BT_CLIENT_EXCHANGE, nCount, (LPCTSTR)m_sAddress );
return TRUE;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -