2008-07-29 11:18:27 -04:00
/*
* libretroshare / src / ft : fttransfermodule . cc
*
* File Transfer for RetroShare .
*
* Copyright 2008 by Robert Fernie .
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Library General Public
* License Version 2 as published by the Free Software Foundation .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Library General Public License for more details .
*
* You should have received a copy of the GNU Library General Public
* License along with this library ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307
* USA .
*
* Please report all bugs and problems to " retroshare@lunamutt.com " .
*
2008-09-08 04:44:37 -04:00
*/
2008-12-07 09:53:57 -05:00
/******
* # define FT_DEBUG 1
* * * * */
2008-09-10 10:12:13 -04:00
2010-08-06 05:40:23 -04:00
# include "retroshare/rsturtle.h"
2008-09-08 04:44:37 -04:00
# include "fttransfermodule.h"
2008-11-04 18:12:53 -05:00
/*************************************************************************
* Notes on file transfer strategy .
* Care must be taken not to overload pipe . best way is to time requests .
* and according adjust data rate .
*
* each peer gets a ' max_rate ' which is decided on the type of transfer .
* - trickle . . .
* - stream . . .
* - max . . .
*
* Each peer is independently managed .
*
* via the functions :
*
*/
const double FT_TM_MAX_PEER_RATE = 1024 * 1024 ; /* 1MB/s */
2008-11-13 18:03:46 -05:00
const uint32_t FT_TM_MAX_RESETS = 5 ;
2008-11-04 18:12:53 -05:00
2008-11-18 20:12:21 -05:00
const uint32_t FT_TM_MINIMUM_CHUNK = 128 ; /* ie 1/8Kb / sec */
2008-11-18 16:22:58 -05:00
const uint32_t FT_TM_RESTART_DOWNLOAD = 20 ; /* 20 seconds */
2008-11-18 20:12:21 -05:00
const uint32_t FT_TM_DOWNLOAD_TIMEOUT = 10 ; /* 10 seconds */
2010-07-29 17:07:07 -04:00
const uint32_t FT_TM_CRC_MAP_MAX_WAIT_PER_GIG = 20 ; /* 20 seconds per gigabyte */
2008-11-18 20:12:21 -05:00
const double FT_TM_MAX_INCREASE = 1.00 ;
const double FT_TM_MIN_INCREASE = - 0.10 ;
const int32_t FT_TM_FAST_RTT = 1.0 ;
const int32_t FT_TM_STD_RTT = 5.0 ;
const int32_t FT_TM_SLOW_RTT = 9.0 ;
2008-11-18 16:22:58 -05:00
2010-09-30 17:02:37 -04:00
const uint32_t FT_TM_CRC_MAP_STATE_NOCHECK = 0 ;
const uint32_t FT_TM_CRC_MAP_STATE_DONT_HAVE = 1 ;
const uint32_t FT_TM_CRC_MAP_STATE_HAVE = 2 ;
2010-07-21 19:14:10 -04:00
2010-07-09 17:04:29 -04:00
# define FT_TM_FLAG_DOWNLOADING 0
# define FT_TM_FLAG_CANCELED 1
# define FT_TM_FLAG_COMPLETE 2
# define FT_TM_FLAG_CHECKING 3
# define FT_TM_FLAG_CHUNK_CRC 4
2010-07-06 01:04:11 -04:00
2008-09-09 09:08:22 -04:00
ftTransferModule : : ftTransferModule ( ftFileCreator * fc , ftDataMultiplex * dm , ftController * c )
2010-07-06 01:04:11 -04:00
: mFileCreator ( fc ) , mMultiplexor ( dm ) , mFtController ( c ) , mFlag ( FT_TM_FLAG_DOWNLOADING )
2008-09-08 04:44:37 -04:00
{
2008-11-02 06:38:11 -05:00
RsStackMutex stack ( tfMtx ) ; /******* STACK LOCKED ******/
2008-09-08 04:44:37 -04:00
mHash = mFileCreator - > getHash ( ) ;
mSize = mFileCreator - > getFileSize ( ) ;
2010-07-09 17:04:29 -04:00
mFileStatus . hash = mHash ;
_hash_thread = NULL ;
2008-09-08 04:44:37 -04:00
// Dummy for Testing (should be handled independantly for
// each peer.
//mChunkSize = 10000;
2008-11-04 18:12:53 -05:00
desiredRate = 1000000 ; /* 1MB/s ??? */
actualRate = 0 ;
2010-09-30 17:02:37 -04:00
_crcmap_state = FT_TM_CRC_MAP_STATE_NOCHECK ;
_crcmap_last_asked_time = 0 ;
2008-09-08 04:44:37 -04:00
}
2008-07-29 11:18:27 -04:00
ftTransferModule : : ~ ftTransferModule ( )
{ }
2008-11-02 06:38:11 -05:00
2008-07-29 11:18:27 -04:00
bool ftTransferModule : : setFileSources ( std : : list < std : : string > peerIds )
{
2008-11-02 06:38:11 -05:00
RsStackMutex stack ( tfMtx ) ; /******* STACK LOCKED ******/
2008-09-08 10:04:10 -04:00
mFileSources . clear ( ) ;
2008-09-10 10:12:13 -04:00
# ifdef FT_DEBUG
std : : cerr < < " ftTransferModule::setFileSources() " ;
std : : cerr < < " List of peers: " ;
# endif
2008-07-29 11:18:27 -04:00
std : : list < std : : string > : : iterator it ;
for ( it = peerIds . begin ( ) ; it ! = peerIds . end ( ) ; it + + )
{
2008-09-10 10:12:13 -04:00
# ifdef FT_DEBUG
std : : cerr < < " \t " < < * it ;
# endif
2008-09-09 09:08:22 -04:00
peerInfo pInfo ( * it ) ;
mFileSources . insert ( std : : pair < std : : string , peerInfo > ( * it , pInfo ) ) ;
2008-07-29 11:18:27 -04:00
}
2008-09-10 10:12:13 -04:00
# ifdef FT_DEBUG
std : : cerr < < std : : endl ;
# endif
2008-09-08 10:04:10 -04:00
return true ;
2008-07-29 11:18:27 -04:00
}
2008-11-02 06:38:11 -05:00
bool ftTransferModule : : getFileSources ( std : : list < std : : string > & peerIds )
{
RsStackMutex stack ( tfMtx ) ; /******* STACK LOCKED ******/
std : : map < std : : string , peerInfo > : : iterator it ;
for ( it = mFileSources . begin ( ) ; it ! = mFileSources . end ( ) ; it + + )
{
peerIds . push_back ( it - > first ) ;
}
return true ;
}
2008-11-13 18:03:46 -05:00
bool ftTransferModule : : addFileSource ( std : : string peerId )
{
RsStackMutex stack ( tfMtx ) ; /******* STACK LOCKED ******/
std : : map < std : : string , peerInfo > : : iterator mit ;
mit = mFileSources . find ( peerId ) ;
if ( mit = = mFileSources . end ( ) )
{
/* add in new source */
peerInfo pInfo ( peerId ) ;
mFileSources . insert ( std : : pair < std : : string , peerInfo > ( peerId , pInfo ) ) ;
mit = mFileSources . find ( peerId ) ;
# ifdef FT_DEBUG
std : : cerr < < " ftTransferModule::addFileSource() " ;
std : : cerr < < " adding peer: " < < peerId < < " to sourceList " ;
std : : cerr < < std : : endl ;
# endif
}
else
{
# ifdef FT_DEBUG
std : : cerr < < " ftTransferModule::addFileSource() " ;
std : : cerr < < " peer: " < < peerId < < " already there " ;
std : : cerr < < std : : endl ;
# endif
}
2008-11-20 19:10:59 -05:00
return true ;
2008-11-13 18:03:46 -05:00
}
2009-06-03 14:47:14 -04:00
bool ftTransferModule : : removeFileSource ( std : : string peerId )
{
RsStackMutex stack ( tfMtx ) ; /******* STACK LOCKED ******/
std : : map < std : : string , peerInfo > : : iterator mit ;
mit = mFileSources . find ( peerId ) ;
if ( mit ! = mFileSources . end ( ) )
{
/* add in new source */
mFileSources . erase ( mit ) ;
# ifdef FT_DEBUG
std : : cerr < < " ftTransferModule::addFileSource(): removing peer: " < < peerId < < " from sourceList " < < std : : cerr < < std : : endl ;
# endif
}
# ifdef FT_DEBUG
else
std : : cerr < < " ftTransferModule::addFileSource(): Should remove peer: " < < peerId < < " , but it's not in the source list. " < < std : : cerr < < std : : endl ;
# endif
return true ;
}
2008-11-13 18:03:46 -05:00
2008-09-09 03:24:06 -04:00
bool ftTransferModule : : setPeerState ( std : : string peerId , uint32_t state , uint32_t maxRate )
2008-07-29 11:18:27 -04:00
{
2008-11-02 06:38:11 -05:00
RsStackMutex stack ( tfMtx ) ; /******* STACK LOCKED ******/
2008-09-09 03:24:06 -04:00
# ifdef FT_DEBUG
std : : cerr < < " ftTransferModule::setPeerState() " ;
std : : cerr < < " peerId: " < < peerId ;
std : : cerr < < " state: " < < state ;
std : : cerr < < " maxRate: " < < maxRate < < std : : endl ;
# endif
2008-09-08 10:04:10 -04:00
std : : map < std : : string , peerInfo > : : iterator mit ;
mit = mFileSources . find ( peerId ) ;
2008-07-29 11:18:27 -04:00
2008-11-13 18:03:46 -05:00
if ( mit = = mFileSources . end ( ) )
{
/* add in new source */
# ifdef FT_DEBUG
std : : cerr < < " ftTransferModule::setPeerState() " ;
std : : cerr < < " adding new peer to sourceList " ;
std : : cerr < < std : : endl ;
# endif
return false ;
}
2008-07-29 11:18:27 -04:00
2008-09-08 10:04:10 -04:00
( mit - > second ) . state = state ;
( mit - > second ) . desiredRate = maxRate ;
2008-11-18 20:12:21 -05:00
// Start it off at zero....
// (mit->second).actualRate=maxRate; /* should give big kick in right direction */
2008-07-29 11:18:27 -04:00
2008-09-09 03:24:06 -04:00
std : : list < std : : string > : : iterator it ;
2008-11-04 18:12:53 -05:00
it = std : : find ( mOnlinePeers . begin ( ) , mOnlinePeers . end ( ) , peerId ) ;
2008-09-09 03:24:06 -04:00
if ( state ! = PQIPEER_NOT_ONLINE )
{
//change to online, add peerId in online peer list
if ( it = = mOnlinePeers . end ( ) ) mOnlinePeers . push_back ( peerId ) ;
}
else
{
//change to offline, remove peerId in online peer list
if ( it ! = mOnlinePeers . end ( ) ) mOnlinePeers . erase ( it ) ;
}
2008-09-08 10:04:10 -04:00
return true ;
2008-07-29 11:18:27 -04:00
}
2008-11-02 06:38:11 -05:00
bool ftTransferModule : : getPeerState ( std : : string peerId , uint32_t & state , uint32_t & tfRate )
{
RsStackMutex stack ( tfMtx ) ; /******* STACK LOCKED ******/
std : : map < std : : string , peerInfo > : : iterator mit ;
mit = mFileSources . find ( peerId ) ;
if ( mit = = mFileSources . end ( ) ) return false ;
state = ( mit - > second ) . state ;
tfRate = ( uint32_t ) ( mit - > second ) . actualRate ;
# ifdef FT_DEBUG
std : : cerr < < " ftTransferModule::getPeerState() " ;
std : : cerr < < " peerId: " < < peerId ;
std : : cerr < < " state: " < < state ;
std : : cerr < < " tfRate: " < < tfRate < < std : : endl ;
# endif
return true ;
}
2008-07-29 11:18:27 -04:00
uint32_t ftTransferModule : : getDataRate ( std : : string peerId )
{
2008-11-02 06:38:11 -05:00
RsStackMutex stack ( tfMtx ) ; /******* STACK LOCKED ******/
2008-07-29 11:18:27 -04:00
std : : map < std : : string , peerInfo > : : iterator mit ;
2008-09-08 10:04:10 -04:00
mit = mFileSources . find ( peerId ) ;
if ( mit = = mFileSources . end ( ) )
{
# ifdef FT_DEBUG
std : : cerr < < " ftTransferModule::getDataRate() " ;
std : : cerr < < " peerId: " < < peerId ;
std : : cerr < < " peer not exist in file sources " < < std : : endl ;
# endif
2008-07-29 11:18:27 -04:00
return 0 ;
2008-09-08 10:04:10 -04:00
}
2008-07-29 11:18:27 -04:00
else
2008-08-17 11:23:11 -04:00
return ( uint32_t ) ( mit - > second ) . actualRate ;
2008-07-29 11:18:27 -04:00
}
2008-09-08 04:44:37 -04:00
//interface to client module
2009-12-08 17:29:52 -05:00
bool ftTransferModule : : recvFileData ( std : : string peerId , uint64_t offset , uint32_t chunk_size , void * data )
2008-09-08 04:44:37 -04:00
{
# ifdef FT_DEBUG
std : : cerr < < " ftTransferModule::recvFileData() " ;
std : : cerr < < " peerId: " < < peerId ;
std : : cerr < < " offset: " < < offset ;
std : : cerr < < " chunksize: " < < chunk_size ;
2008-10-29 16:58:23 -04:00
std : : cerr < < " data: " < < data ;
2008-09-08 04:44:37 -04:00
std : : cerr < < std : : endl ;
# endif
2008-09-08 10:04:10 -04:00
2008-11-04 18:12:53 -05:00
bool ok = false ;
2008-11-02 06:38:11 -05:00
{
RsStackMutex stack ( tfMtx ) ; /******* STACK LOCKED ******/
2008-11-04 18:12:53 -05:00
std : : map < std : : string , peerInfo > : : iterator mit ;
mit = mFileSources . find ( peerId ) ;
2008-09-08 10:04:10 -04:00
2008-11-04 18:12:53 -05:00
if ( mit = = mFileSources . end ( ) )
{
2008-10-22 14:12:58 -04:00
# ifdef FT_DEBUG
2008-11-04 18:12:53 -05:00
std : : cerr < < " ftTransferModule::recvFileData() " ;
std : : cerr < < " peer not found in sources " ;
std : : cerr < < std : : endl ;
2008-10-22 14:12:58 -04:00
# endif
2008-11-04 18:12:53 -05:00
return false ;
}
ok = locked_recvPeerData ( mit - > second , offset , chunk_size , data ) ;
2008-09-08 10:04:10 -04:00
2008-11-02 06:38:11 -05:00
} /***** STACK MUTEX END ****/
2008-11-04 18:12:53 -05:00
if ( ok )
storeData ( offset , chunk_size , data ) ;
2009-06-11 04:09:08 -04:00
free ( data ) ;
2008-11-04 18:12:53 -05:00
return ok ;
2008-09-08 04:44:37 -04:00
}
void ftTransferModule : : requestData ( std : : string peerId , uint64_t offset , uint32_t chunk_size )
{
2008-09-08 10:04:10 -04:00
# ifdef FT_DEBUG
2008-09-08 04:44:37 -04:00
std : : cerr < < " ftTransferModule::requestData() " ;
std : : cerr < < " peerId: " < < peerId ;
2008-09-08 10:04:10 -04:00
std : : cerr < < " hash: " < < mHash ;
std : : cerr < < " size: " < < mSize ;
2008-09-08 04:44:37 -04:00
std : : cerr < < " offset: " < < offset ;
std : : cerr < < " chunk_size: " < < chunk_size ;
std : : cerr < < std : : endl ;
2008-09-08 10:04:10 -04:00
# endif
2008-09-08 04:44:37 -04:00
mMultiplexor - > sendDataRequest ( peerId , mHash , mSize , offset , chunk_size ) ;
}
2009-12-08 17:29:52 -05:00
bool ftTransferModule : : getChunk ( const std : : string & peer_id , uint32_t size_hint , uint64_t & offset , uint32_t & chunk_size )
2008-09-08 04:44:37 -04:00
{
2008-09-08 10:04:10 -04:00
# ifdef FT_DEBUG
2008-09-08 04:44:37 -04:00
std : : cerr < < " ftTransferModule::getChunk() " ;
2008-09-08 10:04:10 -04:00
std : : cerr < < " hash: " < < mHash ;
std : : cerr < < " size: " < < mSize ;
std : : cerr < < " offset: " < < offset ;
2009-12-08 17:29:52 -05:00
std : : cerr < < " size_hint: " < < size_hint ;
2008-09-08 04:44:37 -04:00
std : : cerr < < " chunk_size: " < < chunk_size ;
std : : cerr < < std : : endl ;
2008-09-08 10:04:10 -04:00
# endif
2008-09-08 04:44:37 -04:00
2010-01-11 11:00:42 -05:00
bool source_peer_map_needed ;
2010-02-02 17:15:42 -05:00
2010-02-17 17:10:12 -05:00
bool val = mFileCreator - > getMissingChunk ( peer_id , size_hint , offset , chunk_size , source_peer_map_needed ) ;
2010-01-11 11:00:42 -05:00
if ( source_peer_map_needed )
2010-07-25 15:04:31 -04:00
mMultiplexor - > sendChunkMapRequest ( peer_id , mHash , false ) ;
2008-09-08 04:44:37 -04:00
2008-09-08 10:04:10 -04:00
# ifdef FT_DEBUG
2008-09-08 04:44:37 -04:00
if ( val )
{
std : : cerr < < " ftTransferModule::getChunk() " ;
2008-09-08 10:04:10 -04:00
std : : cerr < < " Answer: Chunk Available " ;
std : : cerr < < " hash: " < < mHash ;
std : : cerr < < " size: " < < mSize ;
std : : cerr < < " offset: " < < offset ;
2008-09-08 04:44:37 -04:00
std : : cerr < < " chunk_size: " < < chunk_size ;
2010-01-11 11:00:42 -05:00
std : : cerr < < " peer map needed = " < < source_peer_map_needed < < std : : endl ;
2008-09-08 04:44:37 -04:00
std : : cerr < < std : : endl ;
}
else
{
std : : cerr < < " ftTransferModule::getChunk() " ;
std : : cerr < < " Answer: No Chunk Available " ;
2010-01-11 11:00:42 -05:00
std : : cerr < < " peer map needed = " < < source_peer_map_needed < < std : : endl ;
2008-09-08 04:44:37 -04:00
std : : cerr < < std : : endl ;
}
2008-09-08 10:04:10 -04:00
# endif
2008-09-08 04:44:37 -04:00
return val ;
}
bool ftTransferModule : : storeData ( uint64_t offset , uint32_t chunk_size , void * data )
{
2008-09-08 10:04:10 -04:00
# ifdef FT_DEBUG
2008-09-08 04:44:37 -04:00
std : : cerr < < " ftTransferModule::storeData() " ;
2008-09-08 10:04:10 -04:00
std : : cerr < < " hash: " < < mHash ;
std : : cerr < < " size: " < < mSize ;
2008-09-08 04:44:37 -04:00
std : : cerr < < " offset: " < < offset ;
std : : cerr < < " chunk_size: " < < chunk_size ;
std : : cerr < < std : : endl ;
2008-09-08 10:04:10 -04:00
# endif
2008-09-08 04:44:37 -04:00
return mFileCreator - > addFileData ( offset , chunk_size , data ) ;
}
2008-09-09 03:24:06 -04:00
bool ftTransferModule : : queryInactive ( )
2008-07-29 11:18:27 -04:00
{
2008-11-02 06:38:11 -05:00
/* NB: Not sure about this lock... might cause deadlock.
*/
2010-07-09 17:04:29 -04:00
{
RsStackMutex stack ( tfMtx ) ; /******* STACK LOCKED ******/
2008-11-02 06:38:11 -05:00
2008-07-29 11:18:27 -04:00
# ifdef FT_DEBUG
2010-07-09 17:04:29 -04:00
std : : cerr < < " ftTransferModule::queryInactive() " < < std : : endl ;
2008-07-29 11:18:27 -04:00
# endif
2008-09-08 10:04:10 -04:00
2010-07-09 17:04:29 -04:00
if ( mFileStatus . stat = = ftFileStatus : : PQIFILE_INIT )
mFileStatus . stat = ftFileStatus : : PQIFILE_DOWNLOADING ;
2008-09-08 04:44:37 -04:00
2010-07-09 17:04:29 -04:00
if ( mFileStatus . stat = = ftFileStatus : : PQIFILE_CHECKING )
return false ;
2010-07-06 01:04:11 -04:00
2010-07-09 17:04:29 -04:00
if ( mFileStatus . stat ! = ftFileStatus : : PQIFILE_DOWNLOADING )
{
if ( mFileStatus . stat = = ftFileStatus : : PQIFILE_FAIL_CANCEL )
mFlag = FT_TM_FLAG_COMPLETE ; //file canceled by user
return false ;
}
2008-09-08 10:04:10 -04:00
2010-07-09 17:04:29 -04:00
std : : map < std : : string , peerInfo > : : iterator mit ;
for ( mit = mFileSources . begin ( ) ; mit ! = mFileSources . end ( ) ; mit + + )
{
locked_tickPeerTransfer ( mit - > second ) ;
}
if ( mFileCreator - > finished ( ) ) // transfer is complete
{
mFileStatus . stat = ftFileStatus : : PQIFILE_CHECKING ;
mFlag = FT_TM_FLAG_CHECKING ;
}
2010-07-06 01:04:11 -04:00
}
2010-02-17 17:10:12 -05:00
2010-07-06 01:04:11 -04:00
return true ;
2008-07-29 11:18:27 -04:00
}
2008-09-08 04:44:37 -04:00
bool ftTransferModule : : cancelTransfer ( )
{
2008-11-02 06:38:11 -05:00
RsStackMutex stack ( tfMtx ) ; /******* STACK LOCKED ******/
2008-09-08 10:04:10 -04:00
mFileStatus . stat = ftFileStatus : : PQIFILE_FAIL_CANCEL ;
return 1 ;
2008-09-08 04:44:37 -04:00
}
2010-07-06 01:04:11 -04:00
bool ftTransferModule : : cancelFileTransferUpward ( )
{
if ( mFtController )
mFtController - > FileCancel ( mHash ) ;
return true ;
}
2008-09-08 04:44:37 -04:00
bool ftTransferModule : : completeFileTransfer ( )
{
2008-11-29 17:03:36 -05:00
# ifdef FT_DEBUG
2008-10-29 16:58:23 -04:00
std : : cerr < < " ftTransferModule::completeFileTransfer() " ;
std : : cerr < < std : : endl ;
2008-11-29 17:03:36 -05:00
# endif
2008-10-29 16:58:23 -04:00
if ( mFtController )
mFtController - > FlagFileComplete ( mHash ) ;
2008-09-08 04:44:37 -04:00
return true ;
}
2008-07-29 11:18:27 -04:00
int ftTransferModule : : tick ( )
{
2009-05-26 17:42:45 -04:00
queryInactive ( ) ;
2008-09-10 10:12:13 -04:00
# ifdef FT_DEBUG
2008-11-04 18:12:53 -05:00
{
RsStackMutex stack ( tfMtx ) ; /******* STACK LOCKED ******/
2008-09-10 10:12:13 -04:00
std : : cerr < < " ftTransferModule::tick() " ;
std : : cerr < < " mFlag: " < < mFlag ;
2008-11-04 18:12:53 -05:00
std : : cerr < < " mHash: " < < mHash ;
std : : cerr < < " mSize: " < < mSize ;
2008-09-10 10:12:13 -04:00
std : : cerr < < std : : endl ;
2008-11-04 18:12:53 -05:00
std : : cerr < < " Peers: " ;
std : : map < std : : string , peerInfo > : : iterator it ;
for ( it = mFileSources . begin ( ) ; it ! = mFileSources . end ( ) ; it + + )
{
std : : cerr < < " " < < it - > first ;
}
std : : cerr < < std : : endl ;
}
2008-09-10 10:12:13 -04:00
# endif
2008-11-02 06:38:11 -05:00
uint32_t flags = 0 ;
{
RsStackMutex stack ( tfMtx ) ; /******* STACK LOCKED ******/
flags = mFlag ;
}
switch ( flags )
2008-09-09 03:24:06 -04:00
{
2010-07-09 17:04:29 -04:00
case FT_TM_FLAG_DOWNLOADING : //file transfer not complete
2008-09-09 03:24:06 -04:00
adjustSpeed ( ) ;
break ;
2010-07-09 17:04:29 -04:00
case FT_TM_FLAG_COMPLETE : //file transfer complete
2008-09-09 03:24:06 -04:00
completeFileTransfer ( ) ;
break ;
2010-07-09 17:04:29 -04:00
case FT_TM_FLAG_CANCELED : //file transfer canceled
2008-09-09 03:24:06 -04:00
break ;
2010-07-09 17:04:29 -04:00
case FT_TM_FLAG_CHECKING : // Check if file hash matches the hashed data
2010-07-06 01:04:11 -04:00
checkFile ( ) ;
break ;
2010-07-09 17:04:29 -04:00
case FT_TM_FLAG_CHUNK_CRC : // File is waiting for CRC32 map. Check if received, and re-set matched chunks
2010-07-06 01:04:11 -04:00
checkCRC ( ) ;
break ;
2008-09-09 03:24:06 -04:00
default :
break ;
}
2008-07-29 11:18:27 -04:00
return 0 ;
}
2008-09-08 04:44:37 -04:00
2010-07-06 01:04:11 -04:00
bool ftTransferModule : : isCheckingHash ( )
{
RsStackMutex stack ( tfMtx ) ; /******* STACK LOCKED ******/
2010-07-09 17:04:29 -04:00
# ifdef FT_DEBUG
std : : cerr < < " isCheckingHash(): mFlag= " < < mFlag < < std : : endl ;
# endif
2010-07-21 19:14:10 -04:00
return mFlag = = FT_TM_FLAG_CHECKING | | mFlag = = FT_TM_FLAG_CHUNK_CRC ;
2010-07-06 01:04:11 -04:00
}
2010-07-09 17:04:29 -04:00
class HashThread : public RsThread
{
public :
HashThread ( ftFileCreator * m )
: _m ( m ) , _finished ( false ) , _hash ( " " ) { }
virtual void run ( )
{
RsStackMutex stack ( _hashThreadMtx ) ;
2010-07-30 17:01:51 -04:00
_m - > hashReceivedData ( _hash ) ;
2010-07-09 17:04:29 -04:00
_finished = true ;
}
std : : string hash ( )
{
RsStackMutex stack ( _hashThreadMtx ) ;
return _hash ;
}
bool finished ( )
{
RsStackMutex stack ( _hashThreadMtx ) ;
return _finished ;
}
private :
RsMutex _hashThreadMtx ;
ftFileCreator * _m ;
bool _finished ;
std : : string _hash ;
} ;
2010-07-06 01:04:11 -04:00
bool ftTransferModule : : checkFile ( )
{
{
RsStackMutex stack ( tfMtx ) ; /******* STACK LOCKED ******/
# ifdef FT_DEBUG
std : : cerr < < " ftTransferModule::checkFile(): checking File " < < mHash < < std : : endl ;
# endif
2010-07-09 17:04:29 -04:00
// if we don't have a hashing thread, create one.
if ( _hash_thread = = NULL )
{
// Note: using new is really important to avoid copy and write errors in the thread.
//
_hash_thread = new HashThread ( mFileCreator ) ;
_hash_thread - > start ( ) ;
# ifdef FT_DEBUG
std : : cerr < < " ftTransferModule::checkFile(): launched hashing thread for file " < < mHash < < std : : endl ;
# endif
return false ;
}
if ( ! _hash_thread - > finished ( ) )
2010-07-06 01:04:11 -04:00
{
2010-07-09 17:04:29 -04:00
# ifdef FT_DEBUG
std : : cerr < < " ftTransferModule::checkFile(): file " < < mHash < < " is being hashed.? " < < std : : endl ;
# endif
2010-07-06 01:04:11 -04:00
return false ;
}
2010-09-30 17:02:37 -04:00
std : : string check_hash ( _hash_thread - > hash ( ) ) ;
delete _hash_thread ;
_hash_thread = NULL ;
if ( check_hash = = mHash )
2010-07-06 01:04:11 -04:00
{
2010-07-09 17:04:29 -04:00
mFlag = FT_TM_FLAG_COMPLETE ; // Transfer is complete.
2010-07-06 01:04:11 -04:00
# ifdef FT_DEBUG
2010-07-09 17:04:29 -04:00
std : : cerr < < " ftTransferModule::checkFile(): hash finished. File verification complete ! Setting mFlag to 1 " < < std : : endl ;
2010-07-06 01:04:11 -04:00
# endif
return true ;
}
}
2010-07-21 19:14:10 -04:00
forceCheck ( ) ;
2010-07-06 01:04:11 -04:00
return false ;
}
2010-07-21 19:14:10 -04:00
void ftTransferModule : : forceCheck ( )
{
RsStackMutex stack ( tfMtx ) ; /******* STACK LOCKED ******/
# ifdef FT_DEBUG
std : : cerr < < " ftTransferModule::forceCheck(): setting flags to force check. " < < std : : endl ;
# endif
2010-07-30 17:01:51 -04:00
mFlag = FT_TM_FLAG_CHUNK_CRC ; // Ask for CRC map.
2010-07-21 19:14:10 -04:00
// setup flags for CRC state machine to work properly
_crcmap_state = FT_TM_CRC_MAP_STATE_DONT_HAVE ;
_crcmap_last_asked_time = 0 ;
}
2010-07-06 01:04:11 -04:00
bool ftTransferModule : : checkCRC ( )
{
2010-07-21 19:14:10 -04:00
// We have a finite state machine here.
//
// The states are, for each chunk, and what should be done:
// DONT_HAVE
// -> ask for the chunk CRC
// ASKED
// -> do nothing
// RECEIVED
// -> check the chunk
// CHECKED
// -> do nothing
//
// CRCs are asked by group of CRC_REQUEST_MAX_SIZE chunks at a time. The response may contain a different
// number of CRCs, depending on the availability.
//
// Server side:
// - Only complete files can compute CRCs, as the CRCs of downloaded chunks in an unchecked file are un-verified.
// - CRCs of files are cached, so that they don't get computed twice.
//
2010-07-06 01:04:11 -04:00
# ifdef FT_DEBUG
std : : cerr < < " ftTransferModule::checkCRC(): looking for CRC32 map. " < < std : : endl ;
# endif
2010-07-21 19:14:10 -04:00
// Loop over chunks. Collect the ones to ask for, and hash the ones received.
// If we have
//
time_t now = time ( NULL ) ;
2010-07-06 01:04:11 -04:00
2010-07-21 19:14:10 -04:00
RsStackMutex stack ( tfMtx ) ; /******* STACK LOCKED ******/
switch ( _crcmap_state )
{
case FT_TM_CRC_MAP_STATE_NOCHECK :
# ifdef FT_DEBUG
std : : cerr < < " ftTransferModule::checkCRC(): state is NOCHECK. Doing nothing. " < < std : : endl ;
# endif
break ;
2010-09-30 17:02:37 -04:00
case FT_TM_CRC_MAP_STATE_DONT_HAVE :
2010-07-21 19:14:10 -04:00
{
2010-09-30 17:02:37 -04:00
// Check wether we have a CRC map or not.
//
std : : cerr < < " FT_TM_CRC_MAP_STATE_ASKED: last time is " < < _crcmap_last_asked_time < < std : : endl ;
std : : cerr < < " FT_TM_CRC_MAP_STATE_ASKED: now is " < < now < < std : : endl ;
uint64_t threshold = ( uint64_t ) ( FT_TM_CRC_MAP_MAX_WAIT_PER_GIG * ( 1 + mSize / float ( 1024ull * 1024ull * 1024ull ) ) ) ;
std : : cerr < < " Threshold is " < < threshold < < std : : endl ;
std : : cerr < < " Limit is " < < ( uint64_t ) _crcmap_last_asked_time + threshold < < std : : endl ;
if ( ( uint64_t ) _crcmap_last_asked_time + threshold > ( uint64_t ) now )
{
2010-07-21 19:14:10 -04:00
# ifdef FT_DEBUG
2010-09-30 17:02:37 -04:00
std : : cerr < < " ftTransferModule::checkCRC(): state is NOCHECK. Doing nothing. " < < std : : endl ;
2010-07-06 01:04:11 -04:00
# endif
2010-09-30 17:02:37 -04:00
break ;
}
2010-07-21 19:14:10 -04:00
// Ask the ones we should ask for. We use a very coarse strategy now: we
// send the request to a random source. We'll make this more sensible
// later.
2010-07-06 01:04:11 -04:00
# ifdef FT_DEBUG
2010-09-30 17:02:37 -04:00
std : : cerr < < " ftTransferModule::checkCRC(): state is DONT_HAVE or last request is too old. Selecting a source for asking a CRC map. " < < std : : endl ;
2010-07-06 01:04:11 -04:00
# endif
2010-09-30 17:02:37 -04:00
if ( mFileSources . empty ( ) )
{
std : : cerr < < " ftTransferModule::checkCRC(): No sources available for checking file " < < mHash < < " : waiting 3 additional sec. " < < std : : endl ;
_crcmap_last_asked_time = now - threshold + 3 ;
break ;
}
2010-07-21 19:14:10 -04:00
2010-07-29 17:07:07 -04:00
int n = rand ( ) % ( mFileSources . size ( ) ) ;
int p = 0 ;
2010-07-21 19:14:10 -04:00
std : : map < std : : string , peerInfo > : : const_iterator mit ;
2010-07-29 17:07:07 -04:00
for ( mit = mFileSources . begin ( ) ; mit ! = mFileSources . end ( ) & & p < n ; + + mit , + + p ) ;
2010-07-21 19:14:10 -04:00
# ifdef FT_DEBUG
2010-07-29 17:07:07 -04:00
std : : cerr < < " ftTransferModule::checkCRC(): sending CRC map request to source " < < mit - > first < < std : : endl ;
2010-07-21 19:14:10 -04:00
# endif
2010-07-29 17:07:07 -04:00
_crcmap_last_asked_time = now ;
mMultiplexor - > sendCRC32MapRequest ( mit - > first , mHash ) ;
2010-07-21 19:14:10 -04:00
}
break ;
case FT_TM_CRC_MAP_STATE_HAVE :
{
# ifdef FT_DEBUG
std : : cerr < < " ftTransferModule::checkCRC(): got a CRC32 map. Matching chunks... " < < std : : endl ;
# endif
// The CRCmap is complete. Let's cross check it with existing chunks in ftFileCreator !
//
uint32_t bad_chunks ;
uint32_t incomplete_chunks ;
if ( ! mFileCreator - > crossCheckChunkMap ( _crcmap , bad_chunks , incomplete_chunks ) )
{
std : : cerr < < " ftTransferModule::checkCRC(): could not check CRC chunks! " < < std : : endl ;
return false ;
}
// go back to download stage, if not all chunks are correct.
//
if ( bad_chunks > 0 )
{
mFlag = FT_TM_FLAG_DOWNLOADING ;
2010-07-30 17:01:51 -04:00
mFileStatus . stat = ftFileStatus : : PQIFILE_DOWNLOADING ;
std : : cerr < < " ftTransferModule::checkCRC(): Done. File has errors: " < < bad_chunks < < " bad chunks found. Restarting download for these chunks only. " < < std : : endl ;
2010-07-21 19:14:10 -04:00
}
else if ( incomplete_chunks > 0 )
{
mFlag = FT_TM_FLAG_DOWNLOADING ;
2010-07-30 17:01:51 -04:00
mFileStatus . stat = ftFileStatus : : PQIFILE_DOWNLOADING ;
2010-07-21 19:14:10 -04:00
# ifdef FT_DEBUG
std : : cerr < < " ftTransferModule::checkCRC(): Done. all chunks ok. Continuing download for remaining chunks. " < < std : : endl ;
# endif
}
else
{
mFlag = FT_TM_FLAG_COMPLETE ;
# ifdef FT_DEBUG
std : : cerr < < " ftTransferModule::checkCRC(): Done. CRC check is ok, file is complete. " < < std : : endl ;
# endif
}
_crcmap_state = FT_TM_CRC_MAP_STATE_NOCHECK ;
}
}
2010-07-06 01:04:11 -04:00
return true ;
}
2010-07-21 19:14:10 -04:00
void ftTransferModule : : addCRC32Map ( const CRC32Map & crc_map )
{
RsStackMutex stack ( tfMtx ) ; /******* STACK LOCKED ******/
// Note: for now, we only send complete CRC32 maps, so the crc_map is always complete. When we
// send partial CRC maps, we will have to check them for completness.
//
_crcmap_state = FT_TM_CRC_MAP_STATE_HAVE ;
_crcmap = crc_map ;
}
2008-09-08 04:44:37 -04:00
void ftTransferModule : : adjustSpeed ( )
{
2008-11-02 06:38:11 -05:00
RsStackMutex stack ( tfMtx ) ; /******* STACK LOCKED ******/
2008-08-06 00:12:36 -04:00
std : : map < std : : string , peerInfo > : : iterator mit ;
2008-11-02 06:38:11 -05:00
2008-11-04 18:12:53 -05:00
actualRate = 0 ;
for ( mit = mFileSources . begin ( ) ; mit ! = mFileSources . end ( ) ; mit + + )
{
2008-11-02 06:38:11 -05:00
# ifdef FT_DEBUG
std : : cerr < < " ftTransferModule::adjustSpeed() " ;
2008-11-04 18:12:53 -05:00
std : : cerr < < " Peer: " < < mit - > first ;
std : : cerr < < " Desired Rate: " < < ( mit - > second ) . desiredRate ;
std : : cerr < < " Actual Rate: " < < ( mit - > second ) . actualRate ;
2008-11-02 06:38:11 -05:00
std : : cerr < < std : : endl ;
# endif
2008-11-04 18:12:53 -05:00
actualRate + = mit - > second . actualRate ;
}
2008-11-02 06:38:11 -05:00
# ifdef FT_DEBUG
2008-11-04 18:12:53 -05:00
std : : cerr < < " ftTransferModule::adjustSpeed() Totals: " ;
std : : cerr < < " Desired Rate: " < < desiredRate < < " Actual Rate: " < < actualRate ;
2008-11-02 06:38:11 -05:00
std : : cerr < < std : : endl ;
# endif
2008-11-04 18:12:53 -05:00
return ;
}
2008-09-08 04:44:37 -04:00
2008-11-04 18:12:53 -05:00
/*******************************************************************************
* Actual Peer Transfer Management Code .
*
* request very tick , at rate
*
*
* */
2008-11-13 18:03:46 -05:00
/* NOTEs on this function...
* 1 ) This is the critical function for deciding the rate at which ft takes place .
* 2 ) Some of the peers might not have the file . . . care must be taken avoid deadlock .
*
* Eg . A edge case which fails badly .
* Small 1 K file ( one chunk ) , with 3 sources ( A , B , C ) . A doesn ' t have file .
* ( a ) request data from A . B & C pause cos no more data needed .
* ( b ) all timeout , chunk reset . . . then back to request again ( a ) and repeat .
* ( c ) all timeout x 5 and are disabled . . . . no transfer , while B & C had it all the time .
*
* To solve this we might need random waiting periods , so each peer can
* be tried .
*
*
*/
2008-11-04 18:12:53 -05:00
bool ftTransferModule : : locked_tickPeerTransfer ( peerInfo & info )
{
/* how long has it been? */
time_t ts = time ( NULL ) ;
int ageRecv = ts - info . recvTS ;
int ageReq = ts - info . lastTS ;
2008-11-09 17:17:20 -05:00
/* if offline - ignore */
if ( ( info . state = = PQIPEER_SUSPEND ) | |
( info . state = = PQIPEER_NOT_ONLINE ) )
{
return false ;
}
2008-11-20 19:10:59 -05:00
if ( ageReq > ( int ) ( FT_TM_RESTART_DOWNLOAD * ( info . nResets + 1 ) ) )
2008-11-04 18:12:53 -05:00
{
2008-11-13 18:03:46 -05:00
if ( info . nResets > 1 ) /* 3rd timeout */
{
/* 90% chance of return false...
* will mean variations in which peer
* starts first . hopefully stop deadlocks .
*/
if ( rand ( ) % 10 ! = 0 )
{
return false ;
}
}
2008-11-04 18:12:53 -05:00
info . state = PQIPEER_DOWNLOADING ;
info . recvTS = ts ; /* reset to activate */
2008-11-13 18:03:46 -05:00
info . nResets + + ;
2008-11-04 18:12:53 -05:00
ageRecv = 0 ;
2008-11-13 18:03:46 -05:00
if ( info . nResets > = FT_TM_MAX_RESETS )
{
/* for this file anyway */
info . state = PQIPEER_NOT_ONLINE ;
return false ;
}
2008-11-04 18:12:53 -05:00
}
2008-11-20 19:10:59 -05:00
if ( ageRecv > ( int ) FT_TM_DOWNLOAD_TIMEOUT )
2008-11-04 18:12:53 -05:00
{
info . state = PQIPEER_IDLE ;
return false ;
}
2009-12-08 17:29:52 -05:00
# ifdef FT_DEBUG
std : : cerr < < " locked_tickPeerTransfer() actual rate (before): " < < info . actualRate < < " , lastTransfers= " < < info . lastTransfers < < std : : endl ;
# endif
2008-11-04 18:12:53 -05:00
/* update rate */
info . actualRate = info . actualRate * 0.75 + 0.25 * info . lastTransfers ;
info . lastTransfers = 0 ;
2008-11-18 20:12:21 -05:00
/****************
* NOTE : If we continually increase the request rate thus : . . .
* uint32_t next_req = info . actualRate * 1.25 ;
*
* then we will achieve max data rate , but we will fill up
* peers out queue and / or network buffers . . . . .
*
* we must therefore monitor the RTT to tell us if this is happening .
*/
/* emergency shutdown if we are stuck in x 1.25 mode
* probably not needed
*/
2010-10-16 17:09:48 -04:00
// csoler: I commented this out because that tends to make some sources
// get stuck into minimal 128 B/s rate, when multiple sources are competiting into the
// same limited bandwidth. I don't think this emergency shutdown is necessary anyway.
//
// if ((info.rttActive) && (ts - info.rttStart > FT_TM_SLOW_RTT))
// {
// if (info.mRateIncrease > 0)
// {
//#ifdef FT_DEBUG
// std::cerr << "!!! - Emergency shutdown because rttActive is true, and age is " << ts - info.rttStart << std::endl ;
//#endif
// info.mRateIncrease = 0;
// info.rttActive = false ; // I've added this to avoid being stuck when rttActive is true
// }
// }
2008-11-18 20:12:21 -05:00
/* request at more than current rate */
uint32_t next_req = info . actualRate * ( 1.0 + info . mRateIncrease ) ;
2008-11-22 12:07:11 -05:00
# ifdef FT_DEBUG
2009-12-08 17:29:52 -05:00
std : : cerr < < " locked_tickPeerTransfer() actual rate (after): " < < actualRate
< < " increase factor= " < < 1.0 + info . mRateIncrease
< < " info.desiredRate= " < < info . desiredRate
< < " info.actualRate= " < < info . actualRate
< < " , next_req= " < < next_req ;
2008-11-22 12:07:11 -05:00
std : : cerr < < std : : endl ;
# endif
2008-11-04 18:12:53 -05:00
if ( next_req > info . desiredRate * 1.1 )
2008-11-22 12:07:11 -05:00
{
2008-11-04 18:12:53 -05:00
next_req = info . desiredRate * 1.1 ;
2008-11-22 12:07:11 -05:00
# ifdef FT_DEBUG
std : : cerr < < " locked_tickPeerTransfer() Reached MaxRate: next_req: " < < next_req ;
std : : cerr < < std : : endl ;
# endif
}
2008-11-04 18:12:53 -05:00
if ( next_req > FT_TM_MAX_PEER_RATE )
2008-11-22 12:07:11 -05:00
{
2008-11-04 18:12:53 -05:00
next_req = FT_TM_MAX_PEER_RATE ;
2008-11-22 12:07:11 -05:00
# ifdef FT_DEBUG
std : : cerr < < " locked_tickPeerTransfer() Reached AbsMaxRate: next_req: " < < next_req ;
std : : cerr < < std : : endl ;
# endif
}
2008-11-04 18:12:53 -05:00
if ( next_req < FT_TM_MINIMUM_CHUNK )
2008-11-22 12:07:11 -05:00
{
2008-11-04 18:12:53 -05:00
next_req = FT_TM_MINIMUM_CHUNK ;
2008-11-22 12:07:11 -05:00
# ifdef FT_DEBUG
std : : cerr < < " locked_tickPeerTransfer() small chunk: next_req: " < < next_req ;
std : : cerr < < std : : endl ;
# endif
}
2008-11-04 18:12:53 -05:00
info . lastTS = ts ;
2008-11-18 20:12:21 -05:00
2008-11-22 12:07:11 -05:00
# ifdef FT_DEBUG
std : : cerr < < " locked_tickPeerTransfer() desired next_req: " < < next_req ;
std : : cerr < < std : : endl ;
# endif
2008-11-04 18:12:53 -05:00
/* do request */
uint64_t req_offset = 0 ;
2009-12-08 17:29:52 -05:00
uint32_t req_size = 0 ;
if ( getChunk ( info . peerId , next_req , req_offset , req_size ) )
2008-11-04 18:12:53 -05:00
{
2009-12-08 17:29:52 -05:00
if ( req_size > 0 )
2008-11-04 18:12:53 -05:00
{
info . state = PQIPEER_DOWNLOADING ;
2009-12-08 17:29:52 -05:00
requestData ( info . peerId , req_offset , req_size ) ;
2008-11-18 20:12:21 -05:00
/* start next rtt measurement */
if ( ! info . rttActive )
{
info . rttStart = ts ;
info . rttActive = true ;
2009-12-08 17:29:52 -05:00
info . rttOffset = req_offset + req_size ;
2008-11-18 20:12:21 -05:00
}
2008-11-04 18:12:53 -05:00
}
else
{
std : : cerr < < " transfermodule::Waiting for available data " ;
std : : cerr < < std : : endl ;
}
}
2008-11-18 20:12:21 -05:00
return true ;
2008-11-04 18:12:53 -05:00
}
//interface to client module
bool ftTransferModule : : locked_recvPeerData ( peerInfo & info , uint64_t offset ,
uint32_t chunk_size , void * data )
{
2008-11-02 06:38:11 -05:00
# ifdef FT_DEBUG
2008-11-04 18:12:53 -05:00
std : : cerr < < " ftTransferModule::locked_recvPeerData() " ;
std : : cerr < < " peerId: " < < info . peerId ;
2009-12-08 17:29:52 -05:00
std : : cerr < < " rttOffset: " < < info . rttOffset ;
std : : cerr < < " lastTransfers: " < < info . lastTransfers ;
2008-11-04 18:12:53 -05:00
std : : cerr < < " offset: " < < offset ;
std : : cerr < < " chunksize: " < < chunk_size ;
std : : cerr < < " data: " < < data ;
2008-11-02 06:38:11 -05:00
std : : cerr < < std : : endl ;
# endif
2008-09-08 04:44:37 -04:00
2008-11-04 18:12:53 -05:00
time_t ts = time ( NULL ) ;
info . recvTS = ts ;
2008-11-13 18:03:46 -05:00
info . nResets = 0 ;
2008-11-04 18:12:53 -05:00
info . state = PQIPEER_DOWNLOADING ;
info . lastTransfers + = chunk_size ;
2008-11-18 20:12:21 -05:00
if ( ( info . rttActive ) & & ( info . rttOffset = = offset + chunk_size ) )
{
2009-12-08 17:29:52 -05:00
/* update tip */
int32_t rtt = time ( NULL ) - info . rttStart ;
2008-11-18 20:12:21 -05:00
2009-12-08 17:29:52 -05:00
/*
* FT_TM_FAST_RTT = 1 sec . mRateIncrease = 1.00
* FT_TM_SLOW_RTT = 9 sec . mRateIncrease = 0
* 11 sec . mRateIncrease = - 0.25
* if it is slower than this allow fast data increase .
* initial guess - linear with rtt .
* change if this leads to wild oscillations
*
*/
2008-11-18 20:12:21 -05:00
2009-12-08 17:29:52 -05:00
info . mRateIncrease = ( FT_TM_SLOW_RTT - rtt ) *
( FT_TM_MAX_INCREASE / ( FT_TM_SLOW_RTT - FT_TM_FAST_RTT ) ) ;
2008-11-18 20:12:21 -05:00
2009-12-08 17:29:52 -05:00
if ( info . mRateIncrease > FT_TM_MAX_INCREASE )
info . mRateIncrease = FT_TM_MAX_INCREASE ;
2008-11-18 20:12:21 -05:00
2009-12-08 17:29:52 -05:00
if ( info . mRateIncrease < FT_TM_MIN_INCREASE )
info . mRateIncrease = FT_TM_MIN_INCREASE ;
info . rtt = rtt ;
info . rttActive = false ;
2008-11-18 20:12:21 -05:00
# ifdef FT_DEBUG
2009-12-08 17:29:52 -05:00
std : : cerr < < " ftTransferModule::locked_recvPeerData() " ;
std : : cerr < < " Updated Rate based on RTT: " < < rtt ;
std : : cerr < < " Rate: " < < info . mRateIncrease ;
std : : cerr < < std : : endl ;
2008-11-18 20:12:21 -05:00
# endif
}
2008-11-04 18:12:53 -05:00
return true ;
}
2008-09-08 04:44:37 -04:00