EnableExplicit Procedure.l Peer_Download_RandomPiece(*Local_TorrentList.Core_Torrent) ; Выбор части для скачивания. Protected Result.l, CountPiece, x, i Protected CurrentCountPiece, Temp;, Result=-2 : x=-1 CountPiece = *Local_TorrentList\TorrentList()\Torrent\CountPiece CurrentCountPiece = *Local_TorrentList\TorrentList()\Torrent\CurrentCountPiece If CountPiece>CurrentCountPiece And CountPiece>0 Result=-1 Temp=CountPiece-1 If G_ProgramSetting\Torrent\Serial_LoadPiece=0 ; Случайный выбор части для загрузки. i=0 Repeat x=Random(Temp) ; Есть ли эта часть у нас? If Torrent_GetArray_MapPiece(*Local_TorrentList\TorrentList()\Torrent\MapFile\MapPiece(), x)=0 Result = x ; Нету этой части у нас - дудем качать ее. ; А есть ли эта часть в текущих скачиваемых? ForEach *Local_TorrentList\TorrentList()\GetPieces_List() If *Local_TorrentList\TorrentList()\GetPieces_List()\Piece = x ; Да, эта часть есть в скачиваемых. Result = -1 Break EndIf Next If Result >= 0 Break EndIf EndIf i+1 Until i>=20 ; Закончились допустимые попытки рамдомного поиска скачиваемой части. EndIf If Result=-1 ; Странно, не удалось найти часть, которую нужно скачать. For i=0 To Temp ; Поищем отсутсвующую часть методом последовательного перебора. ; Есть ли эта часть у нас? If Torrent_GetArray_MapPiece(*Local_TorrentList\TorrentList()\Torrent\MapFile\MapPiece(), i)=0 Result = i ; Нету этой части у нас - дудем качать ее. ; А есть ли эта часть в текущих скачиваемых? ForEach *Local_TorrentList\TorrentList()\GetPieces_List() If *Local_TorrentList\TorrentList()\GetPieces_List()\Piece = i ; Да, эта часть есть в скачиваемых. Result = -1 Break EndIf Next If Result >= 0 Break EndIf EndIf Next i EndIf EndIf ProcedureReturn Result EndProcedure Procedure Peer_Download_FindBestPeer_NoConnect(*Local_TorrentList.Core_Torrent, Piece.l) ; Поиск пира, с которым на данный момент нет конекта и у которого есть максимум частей, которых еще нет у нас. Protected Result, Peer_CountPiece.l, CountPiece.l, TempL.l Protected x, PosBreak, SizeList Result=-1 : x=0 Peer_CountPiece=2 ;0 CountPiece = *Local_TorrentList\TorrentList()\Torrent\CountPiece ; Число частей этого торрента. SizeList=ListSize(*Local_TorrentList\TorrentList()\Network\PeerList()) PosBreak=SizeList If SizeList>0 PosBreak=Random(SizeList-1) EndIf ForEach *Local_TorrentList\TorrentList()\Network\PeerList() ; С этим пиром сейчас не установлена связь, но она устанавливалась ранее и были сохранены данные об имеющихся у него частях. If *Local_TorrentList\TorrentList()\Network\PeerList()\ConnectID=0 And *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_ConnectStatus=#True If *Local_TorrentList\TorrentList()\Network\PeerList()\Err_ConnectCount < #Peer_MaxErrConnect ; Пир вообще доступен? Предыдущие конекты были успешны! If Peer_ConnectTime(*Local_TorrentList) ; У пира есть требуемая часть? If Torrent_GetArray_MapPiece(*Local_TorrentList\TorrentList()\Network\PeerList()\MapPiece(), Piece) ; Сколько частей есть у пира, тех, что еще нет у нас. TempL = Torrent_Compare_PieceArray(*Local_TorrentList\TorrentList()\Torrent\MapFile\MapPiece(), *Local_TorrentList\TorrentList()\Network\PeerList()\MapPiece(), CountPiece) If TempL>Peer_CountPiece Peer_CountPiece=TempL Result=x EndIf EndIf EndIf EndIf EndIf If x>=PosBreak : Break : EndIf x+1 Next ProcedureReturn Result ; Номер пира в списке или -1, если требуемых пиров не найдено. EndProcedure Procedure Peer_OutNewConnect(*Local_TorrentList.Core_Torrent, Piece) ; Установка новых соединений с пирами. Protected Temp, x Protected NewList PeerList.l() Static State.a ; Не превышено ли число соединений для этого торрена и общее число соединений? If *Local_TorrentList\Info\Current_CountConnect < G_ProgramSetting\Lan\MaxConnect If *Local_TorrentList\TorrentList()\Network\CountActivePeer < G_ProgramSetting\Lan\MaxPeer Temp=-1 If State=#False ; Для того чтобы подключались так же новые пиры, а не всегда одни и теже. State=#True Temp=Peer_Download_FindBestPeer_NoConnect(*Local_TorrentList, Piece) ; Поиск пира, с которым на данный момент нет конекта и у которого есть максимум частей, которых нет у нас. Else State=#False EndIf If Temp>=0 ; Найден требуемый пир, среди на данный момент неподключенных, но ранее бывших подключенными. ; Устанавливаем подключение к пиру. Peer_AddConnectPeer(*Local_TorrentList, 0) Else ; Среди ранее подключенных, нт требуемых пиров. Тогда конектимся к ранее неподключенным пирам, может у них чего-то есть нужное нам. x=0 ForEach *Local_TorrentList\TorrentList()\Network\PeerList() ; С этим пиром сейчас не установлена связь, и она не устанавливалась ранее. If *Local_TorrentList\TorrentList()\Network\PeerList()\ConnectID=0 And *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_ConnectStatus=#False ; С этим пиром можно установить соединение. If Peer_ConnectTime(*Local_TorrentList) If AddElement(PeerList()) PeerList() = x EndIf EndIf EndIf x+1 Next ; Случайный выбор пира. x=ListSize(PeerList())-1 If x>=0 If x=0 ; Только один пир найден SelectElement(PeerList(), 0) Else ; Больше одного пира найдено. SelectElement(PeerList(), Random(x)) EndIf SelectElement(*Local_TorrentList\TorrentList()\Network\PeerList(), PeerList()) Peer_AddConnectPeer(*Local_TorrentList, 0) EndIf EndIf Else ; Превышено число соединений для этого торрента, будем отключать пира, который меньше всего данных нам передал. Peer_CloseBadPeer(*Local_TorrentList, #True) EndIf EndIf EndProcedure Procedure.a Peer_Download_Block_TestArray(*Local_TorrentList.Core_Torrent, SizeArray) ; Есть ли еще не запрошенный или нескачанный блок в проверяемой части. Protected Result.a, i Result = #False For i=0 To SizeArray If *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\ConnectID=0 And *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\Point=0 Result = #True ; Блок незапрошен и не скачан. Break EndIf Next i ProcedureReturn Result EndProcedure Procedure.l Peer_Download_Block_Test(*Local_TorrentList.Core_Torrent, Piece.l, SizeArray) ; Проверка, есть ли еще незакачаный блок требуемой части. Если в части все блоки скачаны, то производится поиск еще не скачаных блоков в других частях. Protected Result.l, i;, Index Protected x Result = Piece If Peer_Download_Block_TestArray(*Local_TorrentList, SizeArray)<>#True ; В части нет не скачаанных и незапрошенных блоков. x=#False ForEach *Local_TorrentList\TorrentList()\GetPieces_List() SizeArray = ArraySize(*Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo()) If Peer_Download_Block_TestArray(*Local_TorrentList, SizeArray) = #True x = #True Result=*Local_TorrentList\TorrentList()\GetPieces_List()\Piece ; Блоки этой части будем скачивать. Break EndIf Next If x=#False ; Все блоки всех качаемых частей скачаны или запрошены. Нужно добавитть еще части для скачивания. Result= -1 EndIf EndIf ProcedureReturn Result EndProcedure Procedure Peer_Download_FindBestPeer(*Local_TorrentList.Core_Torrent, Piece.l) ; Поиск пира, с которым установлена связь и у которого максимальная скорость отдачи. Protected NewList PeerList.l() Protected Resilt, MaxInSpeed, Temp, Count Protected PeerNumber Static PeerRdFlag.a Resilt=-1 ; Признак того, что не нашли пиров с треубемыми параметрами. MaxInSpeed=0 : Count=0 : PeerNumber=-1 ForEach *Local_TorrentList\TorrentList()\Network\PeerList() If *Local_TorrentList\TorrentList()\Network\PeerList()\ConnectID And *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_ConnectStatus=#True ; Есть ли у пира эта часть. If Torrent_GetArray_MapPiece(*Local_TorrentList\TorrentList()\Network\PeerList()\MapPiece(), Piece) ; Эта часть у пира есть. ; Мы заинтерисованы в пире? If *Local_TorrentList\TorrentList()\Network\PeerList()\MyStatus\Interested=#False ; Нет, мы в пире незаинтерисованы, исправим. If Peer_SendMessage(*Local_TorrentList, #Peer_Message_Interested) ; Сообщаем пиру что он нам очень и даже очень интересен. *Local_TorrentList\TorrentList()\Network\PeerList()\MyStatus\Interested=#True EndIf Else ; Пир уже знает что он нам интересен ; А не блокирует ли нас пир? If *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_Status\Choke=#False ; ОК, пир нас не сблокирует и судя по всему, пир готов отлать нам даные. ; А не превышен ли предел запросов для этого пира. If *Local_TorrentList\TorrentList()\Network\PeerList()\CurrentRequest < *Local_TorrentList\TorrentList()\Network\PeerList()\MaxRequest;#Peer_MaxPeerRequest ; Раз все нормально, то у пира можно запросить блок. Temp = *Local_TorrentList\TorrentList()\Network\PeerList()\Average_InSpeed ; Средняя скорость приема от пира. If Temp > MaxInSpeed MaxInSpeed=Temp PeerNumber = Count EndIf If AddElement(PeerList()) PeerList() = Count EndIf EndIf Else ; Пир нас блокирует. Напомним ему что он нам интересен. If *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_Interested_Time>0 If *Local_TorrentList\Info\Current_Time - *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_Interested_Time > 30 ; Прошло 30 секунд с последнего напоминания. Peer_SendMessage(*Local_TorrentList, #Peer_Message_Interested) ; Сообщаем пиру что он нам очень и даже очень интересен. *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_Interested_Time = *Local_TorrentList\Info\Current_Time EndIf Else *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_Interested_Time = *Local_TorrentList\Info\Current_Time EndIf EndIf EndIf EndIf EndIf Count+1 Next ; От этого пира будем скачивать блок. If PeerNumber>=0 And MaxInSpeed>=#Peer_BestPeer_MinInSpeed And PeerRdFlag<8 SelectElement(*Local_TorrentList\TorrentList()\Network\PeerList(), PeerNumber) Resilt=PeerNumber PeerRdFlag+1 Else ; Пиров с подходящей скоростью не найдено. If PeerRdFlag>=8 : PeerRdFlag=0 : EndIf Count=ListSize(PeerList())-1 If Count>=0 If Count=0 ; Только один элемет в списке. Temp=0 Else ; Больше одного элемента в списке. Temp=Random(Count) EndIf SelectElement(PeerList(), Temp) PeerNumber=PeerList() SelectElement(*Local_TorrentList\TorrentList()\Network\PeerList(), PeerNumber) Resilt=PeerNumber EndIf EndIf ProcedureReturn Resilt EndProcedure Procedure.b Peer_Download_Block(*Local_TorrentList.Core_Torrent) ; Запрос на скачивание блоков части. Protected Temp, x, Piece.l, i Protected SizeArray, *Point, Result.b Result=0 ; Блок по той или иной причине не удалось запросить, хотя свобождные блоки еще есть. Temp=ListSize(*Local_TorrentList\TorrentList()\GetPieces_List()) If Temp>0 ; Есть загружаемые части. x=Random(Temp-1) SelectElement(*Local_TorrentList\TorrentList()\GetPieces_List(), x) Piece=*Local_TorrentList\TorrentList()\GetPieces_List()\Piece ; Блок вот этой части будем качать. SizeArray=ArraySize(*Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo()) Piece=Peer_Download_Block_Test(*Local_TorrentList, Piece, SizeArray) ; Проверка, есть ли еще незакачаный блок требуемой части. Если в части все блоки скачаны, то производится поиск еще не скачаных блоков в других частях. If Piece>=0 ;x=Peer_Download_SelectPeer(*Local_TorrentList, Piece) ; Выбор пира, которому будет послан запрос на скачивание блока. x=Peer_Download_FindBestPeer(*Local_TorrentList, Piece) ; Поиск пира, с которым установлена связь и у которого максимальная скорость отдачи. If x>=0 SelectElement(*Local_TorrentList\TorrentList()\Network\PeerList(), x) ; Нужно ли что-то скачивать, т. е. есть ли все блоки или нет. For i=0 To SizeArray ; Этот блок еще не запрашивали у пиров. If *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\ConnectID=0 And *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\Point=0 *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\ConnectID = *Local_TorrentList\TorrentList()\Network\PeerList()\ConnectID *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\CurrentTime = *Local_TorrentList\Info\Current_Time *Point = Peer_Create_Request(Piece, *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\begin, *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\length) ; Создаем Request-запрос пиру. If *Point ; Запрос успешно сформирован. If Peer_AddList_OutData(*Local_TorrentList, *Point, 17)=#True ; Отправляем пиру запрос блока. ;x=#True ; Мы запросили этот блок у пира. *Local_TorrentList\TorrentList()\Current_CountBlockDownload + 1 ; Текущее число запросов блоков для этого торранта. *Local_TorrentList\TorrentList()\Network\PeerList()\CountOut_Request + 1 ; Исходящий запрос пиру. *Local_TorrentList\TorrentList()\Network\PeerList()\CurrentRequest + 1 ; Текущее число запросов блоков для пира. *Local_TorrentList\TorrentList()\Network\PeerList()\Active = #True ; Переводим пира в раздел активных. *Local_TorrentList\TorrentList()\Network\PeerList()\PeerUp=#True ; Мы качаем от пира. ;Debug "connect "+Str(*Local_TorrentList\TorrentList()\Network\PeerList()\ConnectID)+" GetBlock "+Str(Piece)+" "+Str(*Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\begin) Result = #True Else *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\ConnectID = 0 EndIf Else *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\ConnectID = 0 EndIf Break ;2 EndIf Next EndIf ;Break Else Result = -1 ; Все блоки уже запрошеня или получены. Ножно добавить новую часть. EndIf Else Result = -1 ; Нет загружаемых частей. Ножно добавить. EndIf ProcedureReturn Result EndProcedure Procedure.a Peer_Download(*Local_TorrentList.Core_Torrent) ; Генерация запросов пирам при скачивании торрентов. Protected Result.a, Piece.l, x, Temp Protected *Point, i, Pos, SizeArray, BlockState.b Result=#False If *Local_TorrentList\TorrentList()\Current_CountBlockDownload < #Peer_MaxRequest_Block ; Текущее число загружаемых блоков (запрошены, но еще не полностью скачаны). BlockState = Peer_Download_Block(*Local_TorrentList.Core_Torrent) If BlockState=-1 ; Запрос на скачивание блоков части. ; Надо загрузать новую часть. ; Есть свободные ячейки для загрузки частей. If ListSize(*Local_TorrentList\TorrentList()\GetPieces_List()) < *Local_TorrentList\TorrentList()\GetPieces_Count ; Есть, поэтому добавим часть в закачку. ; Сгенерируем часть, которую хотим загрузить. Piece = Peer_Download_RandomPiece(*Local_TorrentList.Core_Torrent) If Piece>=0 ; Успешно сгенерировали номер части. ; Посмотрим есть ли требуемая часть у уже подключенных пиров. x=#False If Peer_Download_FindBestPeer(*Local_TorrentList, Piece)>=0 ; Поиск пира, с которым установлена связь и у которого максимальная скорость отдачи. LastElement(*Local_TorrentList\TorrentList()\GetPieces_List()) If AddElement(*Local_TorrentList\TorrentList()\GetPieces_List()) ; Блоки качаем последовательно, он первого и до последнего. ; Это первый блок. *Local_TorrentList\TorrentList()\GetPieces_List()\Piece = Piece *Local_TorrentList\TorrentList()\GetPieces_List()\Time = *Local_TorrentList\Info\Current_Time ; Время запроса части. Необходимо для того, чтобы сбросить по таймауту прием части, котой нет у текущих пиров. If Piece+1=*Local_TorrentList\TorrentList()\Torrent\CountPiece ; Это последняя часть. Temp=Round((*Local_TorrentList\TorrentList()\All_Size-(Piece**Local_TorrentList\TorrentList()\piece_length))/#Peer_PieceLength, #PB_Round_Up)-1 Else ; Это не последняя часть. Temp=Round(*Local_TorrentList\TorrentList()\piece_length/#Peer_PieceLength, #PB_Round_Up)-1 EndIf If Temp>=0 And Temp<1000000 If Temp>0 ReDim *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(Temp) EndIf x=#True ; Мы нащли пира стребуемой частью. SizeArray = Temp Pos=0 For i=0 To SizeArray ; Размечаем блоки в массиве. *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\begin=Pos If i#Peer_PieceLength Temp = #Peer_PieceLength Else If Piece+1=*Local_TorrentList\TorrentList()\Torrent\CountPiece ; Это последняя часть. ;Temp = ((*Local_TorrentList\TorrentList()\All_Size-(Piece**Local_TorrentList\TorrentList()\piece_length))-*Local_TorrentList\TorrentList()\piece_length)-(i*#Peer_PieceLength) Temp = (*Local_TorrentList\TorrentList()\All_Size-(Piece**Local_TorrentList\TorrentList()\piece_length))-(i*#Peer_PieceLength) Else Temp = *Local_TorrentList\TorrentList()\piece_length-(i*#Peer_PieceLength) EndIf EndIf Pos+Temp *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\length=Temp *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\Point = 0 *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\ConnectID=0 *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\CurrentTime=0 Next i Else ; Ошибка - удалить элемнт. DeleteElement(*Local_TorrentList\TorrentList()\GetPieces_List()) EndIf EndIf ;Break Result=#True EndIf ElseIf Piece=-1 ; Не удалось найти ни одну часть, которой у нас нет, либо не скачивается. Peer_EndgameMode_Start(*Local_TorrentList) ; Вызов прецедуры приведет к переходу в режим "Endgame Mode". EndIf EndIf ElseIf BlockState=#True Result=#True EndIf Peer_OutNewConnect(*Local_TorrentList, Piece) ; Установка новых соединений с пирами. EndIf ProcedureReturn Result EndProcedure Procedure Peer_Download_InPeers(*Local_TorrentList.Core_Torrent) ; Эта процедура вызывается после того как пир, установивший входящее соединение, пришлет BitField сообщение. ; Сколько частей есть у пира, тех, что еще нет у нас. If Torrent_Compare_PieceArray(*Local_TorrentList\TorrentList()\Torrent\MapFile\MapPiece(), *Local_TorrentList\TorrentList()\Network\PeerList()\MapPiece(), *Local_TorrentList\TorrentList()\Torrent\CountPiece)>0 ; У пира есть части, которых нет у нас. If Peer_SendMessage(*Local_TorrentList, #Peer_Message_Interested) ; Сообщаем пиру что он нам очень и даже очень интересен. *Local_TorrentList\TorrentList()\Network\PeerList()\MyStatus\Interested=#True EndIf EndIf EndProcedure Procedure Peer_Download_Calc_MaxRequest(*Local_TorrentList.Core_Torrent) ; Расчет максимально допустимого числа невыполненых запрсов для конкретного пира. Protected MaxInSpeed, StepInSpeed, MaxPeerRequest MaxInSpeed=0 : StepInSpeed=0 ForEach *Local_TorrentList\TorrentList()\Network\PeerList() If *Local_TorrentList\TorrentList()\Network\PeerList()\Average_InSpeed > MaxInSpeed ; Средняя входящая скорость пира. MaxInSpeed = *Local_TorrentList\TorrentList()\Network\PeerList()\Average_InSpeed EndIf Next If MaxInSpeed>0 StepInSpeed = Round(MaxInSpeed / #Peer_MaxPeerRequest, #PB_Round_Up) If StepInSpeed>0 ForEach *Local_TorrentList\TorrentList()\Network\PeerList() MaxInSpeed = Round(*Local_TorrentList\TorrentList()\Network\PeerList()\Average_InSpeed / StepInSpeed, #PB_Round_Up) If MaxInSpeed<=0 MaxInSpeed=1 ElseIf MaxInSpeed>#Peer_MaxPeerRequest MaxInSpeed=#Peer_MaxPeerRequest EndIf *Local_TorrentList\TorrentList()\Network\PeerList()\MaxRequest=MaxInSpeed Next EndIf EndIf EndProcedure Procedure Peer_Download_FindSleepPiece(*Local_TorrentList.Core_Torrent) ; Поиск частей, блоки котторых пиры не присылали в течение пределенного времени. Protected SizeArray, i, Temp ForEach *Local_TorrentList\TorrentList()\GetPieces_List() If *Local_TorrentList\Info\Current_Time - *Local_TorrentList\TorrentList()\GetPieces_List()\Time > #Peer_CountTime_SleepPiece ; Превышен максимально допустимый интервал ожидания блоков части. Части нужно изъять из загрузки, освободить память и послать всем пирам сообщение "Cancel". SizeArray=ArraySize(*Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo()) For i=0 To SizeArray If *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\Point FreeMemory(*Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\Point) *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\Point=0 EndIf ; Отмена загрузки блока от пира. Temp = *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\ConnectID If Temp ForEach *Local_TorrentList\TorrentList()\Network\PeerList() If *Local_TorrentList\TorrentList()\Network\PeerList()\ConnectID = Temp Peer_Send_Cancel(*Local_TorrentList, *Local_TorrentList\TorrentList()\GetPieces_List()\Piece, *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\begin, *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\length) Break EndIf Next *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\ConnectID=0 EndIf Next i DeleteElement(*Local_TorrentList\TorrentList()\GetPieces_List()) EndIf Next EndProcedure DisableExplicit ; IDE Options = PureBasic 5.11 (Windows - x86) ; Folding = -- ; EnableXP