EnableExplicit Procedure Peer_ClearQueueList(*Local_TorrentList.Core_Torrent) ; Очистка списка заданий для определеного пира. Текущий элемент с данными пира должен быть выбран в списке. If ListSize(*Local_TorrentList\TorrentList()\Network\PeerList()\QueueList())>0 ForEach *Local_TorrentList\TorrentList()\Network\PeerList()\QueueList() If *Local_TorrentList\TorrentList()\Network\PeerList()\QueueList()\Point FreeMemory(*Local_TorrentList\TorrentList()\Network\PeerList()\QueueList()\Point) *Local_TorrentList\TorrentList()\Network\PeerList()\QueueList()\Point=0 EndIf Next ClearList(*Local_TorrentList\TorrentList()\Network\PeerList()\QueueList()) EndIf *Local_TorrentList\TorrentList()\Network\PeerList()\PosQueue=0 EndProcedure Procedure Peer_CopyInfo_AddPeer(*Local_TorrentList.Core_Torrent) ; Копирование данных пира из основного сипска в локальный. Protected x, i *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_id = G_TorrentList\TorrentList()\Network\PeerList()\Peer_id *Local_TorrentList\TorrentList()\Network\PeerList()\IP$ = G_TorrentList\TorrentList()\Network\PeerList()\IP$ *Local_TorrentList\TorrentList()\Network\PeerList()\ip = G_TorrentList\TorrentList()\Network\PeerList()\ip *Local_TorrentList\TorrentList()\Network\PeerList()\Port = G_TorrentList\TorrentList()\Network\PeerList()\Port *Local_TorrentList\TorrentList()\Network\PeerList()\NumTracker = G_TorrentList\TorrentList()\Network\PeerList()\NumTracker *Local_TorrentList\TorrentList()\Network\PeerList()\Active = #False *Local_TorrentList\TorrentList()\Network\PeerList()\ConnectID = 0 *Local_TorrentList\TorrentList()\Network\PeerList()\Connect_Status = #Null ;#Peer_OutConnect *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_Status\Choke=#True *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_Status\Interested=#False *Local_TorrentList\TorrentList()\Network\PeerList()\MyStatus\Choke=#True *Local_TorrentList\TorrentList()\Network\PeerList()\MyStatus\Interested=#False *Local_TorrentList\TorrentList()\Network\PeerList()\PosQueue = 0 *Local_TorrentList\TorrentList()\Network\PeerList()\CountIn_Request = 0 *Local_TorrentList\TorrentList()\Network\PeerList()\CountOut_Request = 0 *Local_TorrentList\TorrentList()\Network\PeerList()\Total_InBytes=0 *Local_TorrentList\TorrentList()\Network\PeerList()\Total_OutBytes = 0 *Local_TorrentList\TorrentList()\Network\PeerList()\Time_UploadNoConnect = 0 x=0 For i=1 To 10 *Local_TorrentList\TorrentList()\Network\PeerList()\ReceivePoint = AllocateMemory(128) If *Local_TorrentList\TorrentList()\Network\PeerList()\ReceivePoint x=128 Break Else Delay(2) EndIf Next i *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Pos=0 *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Info_Len=0 *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Len=x *Local_TorrentList\TorrentList()\Network\PeerList()\InCon=#False x=ArraySize(G_TorrentList\TorrentList()\Torrent\MapFile\MapPiece()) If x<1 : x=0 : EndIf ReDim *Local_TorrentList\TorrentList()\Network\PeerList()\MapPiece(x) For i=0 To x *Local_TorrentList\TorrentList()\Network\PeerList()\MapPiece(i) = 0 ; Отмечаем что у пира нет чатей торрента. Далее have и bitfield собщения заполнят этот массив. Next i *Local_TorrentList\TorrentList()\Network\PeerList()\PeerDown = #False *Local_TorrentList\TorrentList()\Network\PeerList()\PeerUp = #False *Local_TorrentList\TorrentList()\Network\PeerList()\CountIn_Request = 0 EndProcedure Procedure Peer_DelPeel_GetPieces_List(*Local_TorrentList.Core_Torrent, Connect) ; Удаление пира из списка запросов блоков, частей этого торрента. Protected Temp, i, x ; Список запросов блоков, частей этого торрента. If ListSize(*Local_TorrentList\TorrentList()\GetPieces_List())>0 Temp=ListIndex(*Local_TorrentList\TorrentList()\GetPieces_List()) ForEach *Local_TorrentList\TorrentList()\GetPieces_List() x = ArraySize(*Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo()) For i=0 To x If *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\ConnectID = Connect If *Local_TorrentList\TorrentList()\Current_CountBlockDownload>0 *Local_TorrentList\TorrentList()\Current_CountBlockDownload - 1 EndIf If *Local_TorrentList\TorrentList()\Network\PeerList()\CurrentRequest>0 *Local_TorrentList\TorrentList()\Network\PeerList()\CurrentRequest - 1 EndIf *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\ConnectID = 0 *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\CurrentTime=0 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 EndIf Next i Next If Temp>=0 ; Есть текущий элемент. SelectElement(*Local_TorrentList\TorrentList()\GetPieces_List(), Temp) ; Восстанавливаем элемент EndIf EndIf EndProcedure Procedure Peer_ClearPeer(*Local_TorrentList.Core_Torrent) ; Деактивация текущего пира. В списке должен быть выбран требуемый пир. Protected x, i, Temp, Connect If *Local_TorrentList\TorrentList()\Network\PeerList()\PeerDown=#True ; Этот пир был в числе качающих от нас. *Local_TorrentList\TorrentList()\Network\CountUploadPeer-1 ; Освободился слок для скачивания. Теперь его может занять другой пир. *Local_TorrentList\TorrentList()\Network\PeerList()\PeerDown=#False EndIf *Local_TorrentList\TorrentList()\Network\PeerList()\PeerUp = #False Connect = *Local_TorrentList\TorrentList()\Network\PeerList()\ConnectID If Connect Peer_DelPeel_GetPieces_List(*Local_TorrentList.Core_Torrent, Connect) ; Удаление пира из списка запросов блоков, частей этого торрента. CloseNetworkConnection(Connect) *Local_TorrentList\Info\Current_CountConnect - 1 ; Общее число соединений. *Local_TorrentList\TorrentList()\Network\CountActivePeer-1 ; Общее число пиров текущего торрента, с которыми установлен коннект. EndIf *Local_TorrentList\TorrentList()\Network\PeerList()\ConnectID=0 *Local_TorrentList\TorrentList()\Network\PeerList()\Connect_Status=#Null Peer_ClearQueueList(*Local_TorrentList) ; Очистить список сообщений. If *Local_TorrentList\TorrentList()\Network\PeerList()\ReceivePoint FreeMemory(*Local_TorrentList\TorrentList()\Network\PeerList()\ReceivePoint) *Local_TorrentList\TorrentList()\Network\PeerList()\ReceivePoint = 0 EndIf *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Pos = 0 *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Len = 0 *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Info_Len=0 *Local_TorrentList\TorrentList()\Network\PeerList()\InCon=#False *Local_TorrentList\TorrentList()\Network\PeerList()\Err_ConnectCount=0 *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_Status\Choke=#True *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_Status\Interested=#False *Local_TorrentList\TorrentList()\Network\PeerList()\MyStatus\Choke=#True *Local_TorrentList\TorrentList()\Network\PeerList()\MyStatus\Interested=#False *Local_TorrentList\TorrentList()\Network\PeerList()\Active=#False EndProcedure Procedure Peer_ReallocateMemory(*Local_TorrentList.Core_Torrent, PlusSize) ; Определение нужно ли увеличить память под данные, принимаемые от пира. Protected Len, Pos, NewLen, *Point Protected *NewPoint, i Len = *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Len Pos = *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Pos *Point = *Local_TorrentList\TorrentList()\Network\PeerList()\ReceivePoint If PlusSize>0 NewLen = 0 If Pos>Len Or Len-Pos < PlusSize Or *Point=0; Нужно изменить (увеличить) размер памяти. NewLen = Len+PlusSize+100 If NewLen> 400000 ; Глюк. Даже теоритически столько памяти не нужно. Peer_ClearPeer(*Local_TorrentList) ; Отключаем пира и осовбождаем память, которую он использует. NewLen = 0 Len=0 EndIf ElseIf Len-Pos > PlusSize+1000 ; Памяти выдленено слишком много и надо уменьшить. NewLen = PlusSize+Pos+80;0 EndIf If NewLen > 0 For i=1 To 4 *NewPoint = ReAllocateMemory(*Point, NewLen) If *NewPoint *Local_TorrentList\TorrentList()\Network\PeerList()\ReceivePoint = *NewPoint *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Len = NewLen If *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Info_Len>NewLen *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Info_Len=NewLen EndIf Len = NewLen Break Else Delay(4) EndIf Next i EndIf EndIf ProcedureReturn Len EndProcedure Procedure Peer_AddInMem(*Local_TorrentList.Core_Torrent, *Mem, Size) ; Добавление принятых жанных от пира, в память еще необработанных сообщений. Protected NewLen NewLen=Peer_ReallocateMemory(*Local_TorrentList, Size) ; Выделение памяти If NewLen>0 And *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Len >= Size+*Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Pos ; Память выделена CopyMemory(*Mem, *Local_TorrentList\TorrentList()\Network\PeerList()\ReceivePoint+*Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Pos, Size) *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Pos + Size *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Info_Len + Size EndIf EndProcedure Procedure Peer_DelInMem(*Local_TorrentList.Core_Torrent, MinusSize) ; Удаление принятых жанных от пира, из памяти после успешной обработки сообщения. If *Local_TorrentList\TorrentList()\Network\PeerList()\ReceivePoint ;If *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Len <= MinusSize If *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Pos <= MinusSize *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Pos = 0 Else ;MoveMemory(*Local_TorrentList\TorrentList()\Network\PeerList()\ReceivePoint+MinusSize, *Local_TorrentList\TorrentList()\Network\PeerList()\ReceivePoint, *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Len-MinusSize) If *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Pos>MinusSize MoveMemory(*Local_TorrentList\TorrentList()\Network\PeerList()\ReceivePoint+MinusSize, *Local_TorrentList\TorrentList()\Network\PeerList()\ReceivePoint, *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Pos-MinusSize) ; EndIf *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Pos - MinusSize If *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Pos<0 *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Pos=0 EndIf *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Info_Len - MinusSize If *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Info_Len<0 *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Info_Len=0 EndIf EndIf EndIf EndProcedure Procedure.a Peer_In_Handshake(*Point, Size, *Info.Peer_Handshake) ; Так называемое "Рукопожатие" при установке коннекта пира с клиентом (входящее соединение). Protected Result.a, Len, MemPos.a Result = #False If *Point And *Info And Size>=68;49 Len=PeekA(*Point) If Len=19 ;And Len+49>=68 MemPos+1 If CompareMemory(*Point+MemPos, ?Peer_Handshake_m, Len)=1 MemPos+Len MemPos+8 ; Зарезервированые байты. CopyMemory(*Point+MemPos, @*Info\INFO_Hash, 20) : MemPos+20 CopyMemory(*Point+MemPos, @*Info\Client, 20) Result = #True EndIf EndIf EndIf ProcedureReturn Result EndProcedure Procedure.a Peer_AddList_OutData(*Local_TorrentList.Core_Torrent, *Mem, Size) ; Добавление данных в список отправки сообщений пирам. Protected Result.a Result = #False If ListSize(*Local_TorrentList\TorrentList()\Network\PeerList()\QueueList())<1000 ;0 If *Mem And Size>0 If Size=17 And PeekA(*Mem+4)=8 ; Это "Cancel" сообщение. FirstElement(*Local_TorrentList\TorrentList()\Network\PeerList()\QueueList()) Else LastElement(*Local_TorrentList\TorrentList()\Network\PeerList()\QueueList()) ; Перешли в конец списка. EndIf If AddElement(*Local_TorrentList\TorrentList()\Network\PeerList()\QueueList()) *Local_TorrentList\TorrentList()\Network\PeerList()\QueueList()\Len = Size *Local_TorrentList\TorrentList()\Network\PeerList()\QueueList()\Point = *Mem Result = #True If *Local_TorrentList\TorrentList()\Network\PeerList()\Connect_Status = #Peer_OutConnect *Local_TorrentList\TorrentList()\Network\PeerList()\TimeOut = 0 EndIf Else FreeMemory(*Mem) EndIf ElseIf *Mem FreeMemory(*Mem) EndIf Else ; Список переполнен! If *Mem : FreeMemory(*Mem) : EndIf Peer_ClearPeer(*Local_TorrentList) EndIf ProcedureReturn Result EndProcedure Procedure.l Peer_SendData(*Local_TorrentList.Core_Torrent, *Counter) ; Передача в сеть данных текущего пира. Перед вызовом процедуры, нужно выбрать текущие элементы торрента и пира. Protected CountSendBytes.l, Pos.l, Len.l, SendLen.l Protected Connect.i, *Point, Command.a, Err CountSendBytes=0 Pos = *Local_TorrentList\TorrentList()\Network\PeerList()\PosQueue ; Текущая позиция в первом элементе списка. Connect = *Local_TorrentList\TorrentList()\Network\PeerList()\ConnectID If Connect If ListSize(*Local_TorrentList\TorrentList()\Network\PeerList()\QueueList())>0 ForEach *Local_TorrentList\TorrentList()\Network\PeerList()\QueueList() Len = *Local_TorrentList\TorrentList()\Network\PeerList()\QueueList()\Len *Point = *Local_TorrentList\TorrentList()\Network\PeerList()\QueueList()\Point If Pos>=Len Or Len<=0 Or *Point=0 ; Похоже что данные были переданы. If *Point FreeMemory(*Point) EndIf DeleteElement(*Local_TorrentList\TorrentList()\Network\PeerList()\QueueList()) Pos=0 *Local_TorrentList\TorrentList()\Network\PeerList()\PosQueue = 0 Continue EndIf If Len-Pos>32767 SendLen=32767+Pos Else SendLen=Len EndIf If Len>4 Command=PeekA(*Point+4);+Pos Else Command=0 EndIf Err=0 CountSendBytes = SendNetworkData(Connect, *Point+Pos, SendLen-Pos) If CountSendBytes = -1 Err=WSAGetLastError_() EndIf If CountSendBytes>0 *Local_TorrentList\TorrentList()\Network\PeerList()\PosQueue+CountSendBytes If Len>5 If Command=7 ; Сообщение Piece, т. е. передача блока пиру. *Local_TorrentList\TorrentList()\Network\PeerList()\OutSpeed + CountSendBytes ; Исхлдящая скорость. EndIf EndIf If *Counter If Command=6 Or Command=7 PokeL(*Counter, PeekL(*Counter)+4) EndIf EndIf ElseIf CountSendBytes = -1 ; Ошибка отправки данных. If Err=6 Or Err=10024 Or Err=10038 Or Err=10050 Or Err=10051 Or Err=10053 Or Err=10054 Peer_ClearPeer(*Local_TorrentList) ; Пира в топку. CompilerIf #Debug_Mode = #True If Err<>10054 AddGadgetItem(#MainWin_Panel_Info_ListIcon_Log, -1, TimeLog()+"Коннект: "+Str(Connect)+" ошибка: "+Str(Err), ImageID(#MainWin_Panel_Info_Icon_Log_Warning)) EndIf CompilerEndIf *Local_TorrentList\TorrentList()\Network\PeerList()\PosQueue = 0 EndIf EndIf If*Local_TorrentList\TorrentList()\Network\PeerList()\Connect_Status = #Peer_OutConnect *Local_TorrentList\TorrentList()\Network\PeerList()\TimeOut = 0 EndIf Break Next EndIf Else ; Если нет связи с пиром. If ListSize(*Local_TorrentList\TorrentList()\Network\PeerList()\QueueList())>0 ForEach *Local_TorrentList\TorrentList()\Network\PeerList()\QueueList() *Point = *Local_TorrentList\TorrentList()\Network\PeerList()\QueueList()\Point If *Point FreeMemory(*Point) EndIf DeleteElement(*Local_TorrentList\TorrentList()\Network\PeerList()\QueueList()) Next EndIf *Local_TorrentList\TorrentList()\Network\PeerList()\PosQueue = 0 EndIf ProcedureReturn CountSendBytes EndProcedure Procedure Peer_Create_Handshake(*Local_TorrentList.Core_Torrent) ; Создание сообщения "Рукопожатия" для пира. Protected *Point, Len, MemPos.a *Point = AllocateMemory(68) If *Point FillMemory(*Point, 68, 0) PokeA(*Point, 19) : MemPos=1 CopyMemory(?Peer_Handshake_m, *Point+MemPos, 19) : MemPos+19+8 CopyMemory(@*Local_TorrentList\TorrentList()\INFO_HashBin[0], *Point+MemPos, 20) : MemPos+20 CopyMemory(@G_ProgramMiscInfo\Network\peer_id_bin[0], *Point+MemPos, 20) EndIf ProcedureReturn *Point EndProcedure ; have: Procedure Peer_Create_Have(Piece.l) ; Создание соощения "Have", информирующиее пира что клиент скачал кусок. Protected *Result *Result=AllocateMemory(10) If *Result PokeL(*Result,$05000000) PokeA(*Result+4, 5) PokeL(*Result+5, ReverseLong(Piece)) EndIf ProcedureReturn 0;*Result EndProcedure ; Procedure Peer_Create_Request(Piece.l, begin.l, length.l) ; Создание запроса пиру, для скачивания требуемого блока, выбраного куска. Protected *Result *Result=AllocateMemory(17) If *Result PokeL(*Result, $0D000000) PokeA(*Result+4, 6) PokeL(*Result+5, ReverseLong(Piece)) PokeL(*Result+9, ReverseLong(begin)) PokeL(*Result+13,ReverseLong(length)) EndIf ProcedureReturn *Result EndProcedure ; Создание отчета о имеющихся частях для пира. Procedure Peer_Create_BitField(*Local_TorrentList.Core_Torrent, *Size.Long) ; Битовое поле в котором каждый бит, обозначает есть ли кусок у пира: 1- есть: 0 - куска нет. Protected *Point, MemLen.l, MessageLen, SizeArray Protected MemPos, i, Byte.a, Temp *Point = 0 If *Local_TorrentList\TorrentList()\Torrent\CurrentCountPiece>0 ; Отсылаем это сообщение только если есть куски. SizeArray = ArraySize(*Local_TorrentList\TorrentList()\Torrent\MapFile\MapPiece()) ; -1 MemLen=4+1+SizeArray *Point = AllocateMemory(MemLen) If *Point PokeL(*Point, ReverseLong(MemLen-4)) : MemPos=4 ; Длина пакета. PokeA(*Point+MemPos, 5) : MemPos+1 ; Команда "BitField". Temp = MemLen-1 For i=MemPos To Temp Byte = *Local_TorrentList\TorrentList()\Torrent\MapFile\MapPiece(i-MemPos) PokeA(*Point+i, ReverseBits_Byte(Byte)) Next i EndIf If *Size *Size\l = MemLen EndIf EndIf ProcedureReturn *Point EndProcedure ; piece: Procedure Peer_Send_Piece(*Local_TorrentList.Core_Torrent, index.l, begin.l, length.l, *MemBlock) ; Передача пиру запрошеного им блока. Protected *Point, MemPos.l, MemLen.l, TempL.l MemPos=0 MemLen=4+9+length If MemLen>0 And MemLen< 200000 ;10*1024*1024 *Point=AllocateMemory(MemLen+2) If *Point FillMemory(*Point, MemLen, 0) TempL=MemLen-4 PokeL(*Point, ReverseLong(TempL)) : MemPos+4 ; Длина пакета. PokeA(*Point+MemPos, 7) : MemPos+1 ; Сообщение "piece". PokeL(*Point+MemPos, ReverseLong(index)) : MemPos+4 ; Номер части торрента. PokeL(*Point+MemPos, ReverseLong(begin)) : MemPos+4 ; Смецение в байтах от начала части. CopyMemory(*MemBlock, *Point+MemPos, length) ; Запрошенный блок. Peer_AddList_OutData(*Local_TorrentList, *Point, MemLen) ; Добавляем данные в очередь передачи для текущего пира. EndIf EndIf EndProcedure ; cancel: Procedure Peer_Send_Cancel(*Local_TorrentList.Core_Torrent, index.l, begin.l, length.l) ; Отмена загрузки блока от пира. Protected *Point;, MemPos.l, MemLen.l, TempL.l *Point=AllocateMemory(17) If *Point PokeL(*Point, $0D000000) PokeA(*Point+4, 8) PokeL(*Point+5, ReverseLong(index)) PokeL(*Point+9, ReverseLong(begin)) PokeL(*Point+13,ReverseLong(length)) Peer_AddList_OutData(*Local_TorrentList, *Point, 17) ; Добавляем данные в очередь передачи для текущего пира. EndIf ;ProcedureReturn EndProcedure Procedure Peer_In_SendStartInfo(*Local_TorrentList.Core_Torrent) ; Эта процедура вызывается для генерации "Рукопожатия" и списка имеющихся частей для пира, установившего входяще соединение. Protected *Point, Size.Long *Point = Peer_Create_Handshake(*Local_TorrentList) If *Point ; Успешное генерирование "Рукопожатия". If Peer_AddList_OutData(*Local_TorrentList, *Point, 68) = #True ; Добавить в список вывода данных. Size\l=0 *Point = Peer_Create_BitField(*Local_TorrentList, @Size) ; Создание отчета о имеющихся частях для пира. If *Point If Size\l>5 Peer_AddList_OutData(*Local_TorrentList, *Point, Size\l) Else FreeMemory(*Point) EndIf EndIf Else ; В случае, ошибки, разорвать связь с пиром. Peer_ClearPeer(*Local_TorrentList) EndIf Else ; В случае, ошибки, разорвать связь с пиром. Peer_ClearPeer(*Local_TorrentList) EndIf EndProcedure Procedure.a Peer_SendMessage(*Local_TorrentList.Core_Torrent, MessageID.a) ; Посылка сообщений типа KeepAlive, Choke и др. Protected *Buffer, Len Protected Pos, SendByte, i, Result.a Result = #False If MessageID>=#Peer_Message_Choke And MessageID<=#Peer_Message_KeepAlive *Buffer=AllocateMemory(8) If *Buffer Len=0 : Pos=0 If MessageID=#Peer_Message_KeepAlive PokeL(*Buffer, 0) Len=4 Else PokeL(*Buffer, $01000000) ; Длина 1 байт в big-endian разположении байт. PokeA(*Buffer+4, MessageID) Len=5 EndIf If Peer_AddList_OutData(*Local_TorrentList, *Buffer, Len)=#True Result = #True EndIf EndIf EndIf ProcedureReturn Result EndProcedure Procedure Peer_StopTorrent(*Local_TorrentList.Core_Torrent, DelState.a=#False) ; Остановка скачивания или раздачи торрента. Protected SizeArray, i, Temp, INFO_Hash.s, x Protected WSACloseEvent.WSACloseEvent=G_ProgramMiscInfo\Network\WSA\WSACloseEvent *Local_TorrentList\TorrentList()\Current_CountBlockDownload = 0 ; Текущее число загружаемых блоков. *Local_TorrentList\TorrentList()\Endgame_Mode = #False ForEach *Local_TorrentList\TorrentList()\Network\PeerList() ; Разрыв связи со всеми пирами торрента. Peer_ClearPeer(*Local_TorrentList) *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_ConnectStatus = #False *Local_TorrentList\TorrentList()\Network\PeerList()\Err_ConnectCount = 0 ; Число ошибок соединения с пиром. ; Обнуление информации об имеющихся кусках у пиров. x=ArraySize(*Local_TorrentList\TorrentList()\Network\PeerList()\MapPiece()) For i=0 To x *Local_TorrentList\TorrentList()\Network\PeerList()\MapPiece(i) = 0 ; Отмечаем что у пира нет чатей торрента. Далее have и bitfield собщения заполнят этот массив. Next i Next ; Закрытие файов торрента. SizeArray = ArraySize(*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles())-1 For i=0 To SizeArray Temp=*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileID If Temp If IsFile(Temp) CloseFile(Temp) EndIf *Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileID=0 *Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\WriteStatus=#False EndIf Next i ForEach *Local_TorrentList\TorrentList()\File_PieceBuffer() ; Освобождение памяти кеширвания частей торрента, считаных из файла (раздача). If *Local_TorrentList\TorrentList()\File_PieceBuffer()\Point FreeMemory(*Local_TorrentList\TorrentList()\File_PieceBuffer()\Point) *Local_TorrentList\TorrentList()\File_PieceBuffer()\Point=0 EndIf Next ClearList(*Local_TorrentList\TorrentList()\File_PieceBuffer()) ForEach *Local_TorrentList\TorrentList()\GetPieces_List() ; Список скачиваемых частей (скачивание). Temp=ArraySize(*Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo()) For i=0 To Temp 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 Next i Next ClearList(*Local_TorrentList\TorrentList()\GetPieces_List()) INFO_Hash = *Local_TorrentList\TorrentList()\INFO_Hash ; Очистка списка заданий подключения к пирам. LockMutex(G_ProgramMiscInfo\Network\ConnectPeer_Mutex) ForEach G_ProgramMiscInfo\Network\ConnectPeer_List() If G_ProgramMiscInfo\Network\ConnectPeer_List()\INFO_Hash = INFO_Hash DeleteElement(G_ProgramMiscInfo\Network\ConnectPeer_List()) EndIf Next ForEach G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List() If G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List()\INFO_Hash = INFO_Hash If G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List()\ConnectID CloseNetworkConnection(G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List()\ConnectID) G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List()\ConnectID=0 EndIf DeleteElement(G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List()) EndIf Next ForEach G_ProgramMiscInfo\Network\TestConnect() If G_ProgramMiscInfo\Network\TestConnect()\INFO_Hash = INFO_Hash If G_ProgramMiscInfo\Network\TestConnect()\ConnectID CloseNetworkConnection(G_ProgramMiscInfo\Network\TestConnect()\ConnectID) G_ProgramMiscInfo\Network\TestConnect()\ConnectID=0 EndIf If G_ProgramMiscInfo\Network\TestConnect()\hEvent WSACloseEvent(G_ProgramMiscInfo\Network\TestConnect()\hEvent) G_ProgramMiscInfo\Network\TestConnect()\hEvent=0 EndIf DeleteElement(G_ProgramMiscInfo\Network\TestConnect()) EndIf Next UnlockMutex(G_ProgramMiscInfo\Network\ConnectPeer_Mutex) If DelState<>#True ForEach G_TorrentList\TorrentList()\Network\PeerList() G_TorrentList\TorrentList()\Network\PeerList()\Active=#False Next ClearList(G_TorrentList\TorrentList()\BlockList()) EndIf If ListIndex(G_TorrentList\TorrentList())>=0 G_TorrentList\TorrentList()\Network_IO\RemainsTime = -1 ; Вредя до окончания скачивания. G_TorrentList\TorrentList()\Average_InSpeed = 0 G_TorrentList\TorrentList()\Average_OutSpeed = 0 G_TorrentList\TorrentList()\Network_IO\InSpeed = 0 G_TorrentList\TorrentList()\Network_IO\InSpeed = 0 G_TorrentList\TorrentList()\Network_IO\Seeds = 0 G_TorrentList\TorrentList()\Network_IO\ActivePeers = 0 ClearList(G_TorrentList\TorrentList()\Average_InSpeed_List()) ClearList(G_TorrentList\TorrentList()\Average_OutSpeed_List()) EndIf EndProcedure Procedure Peer_StartTorrent(*Local_TorrentList.Core_Torrent) ; Запуск скачивания или раздачи торента. Peer_StopTorrent(*Local_TorrentList.Core_Torrent) ; Остановка скачивания или раздачи торрента. *Local_TorrentList\TorrentList()\Current_CountBlockDownload = 0 ; Текущее число загружаемых блоков. ForEach *Local_TorrentList\TorrentList()\Network\PeerList() *Local_TorrentList\TorrentList()\Network\PeerList()\CountIn_Request=0 *Local_TorrentList\TorrentList()\Network\PeerList()\CountOut_Request=0 *Local_TorrentList\TorrentList()\Network\PeerList()\CountHave=0 *Local_TorrentList\TorrentList()\Network\PeerList()\PosQueue=0 Next EndProcedure Procedure Peer_FinishedTorrent(*Local_TorrentList.Core_Torrent) ; Вызывается при завершении скачивания. Protected i, Temp, SizeArray, INFO_Hash.s ; Оповищение пиров что мы в них незаинтерисованы. ForEach *Local_TorrentList\TorrentList()\Network\PeerList() ; Мы были заинтерисованы в этом пире. If *Local_TorrentList\TorrentList()\Network\PeerList()\MyStatus\Interested=#True If *Local_TorrentList\TorrentList()\Network\PeerList()\ConnectID If *Local_TorrentList\TorrentList()\Network\PeerList()\Active = #True And *Local_TorrentList\TorrentList()\Network\PeerList()\PeerDown<>#True ; Пир от нас не качает поэтому делает его пасивным. Peer_ClearPeer(*Local_TorrentList) ; Деактивация текущего пира. INFO_Hash = *Local_TorrentList\TorrentList()\INFO_Hash ; Убираем пиров из списка подключений. LockMutex(G_ProgramMiscInfo\Network\ConnectPeer_Mutex) ForEach G_ProgramMiscInfo\Network\ConnectPeer_List() If G_ProgramMiscInfo\Network\ConnectPeer_List()\INFO_Hash = INFO_Hash DeleteElement(G_ProgramMiscInfo\Network\ConnectPeer_List()) EndIf Next ForEach G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List() If G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List()\INFO_Hash = INFO_Hash If G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List()\ConnectID FreeMemory(G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List()\ConnectID) G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List()\ConnectID=0 EndIf DeleteElement(G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List()) EndIf Next UnlockMutex(G_ProgramMiscInfo\Network\ConnectPeer_Mutex) EndIf EndIf EndIf ; Вы уже в нем незаинтерисованы. *Local_TorrentList\TorrentList()\Network\PeerList()\MyStatus\Interested=#False Next ForEach *Local_TorrentList\TorrentList()\GetPieces_List() ; Список скачиваемых частей (скачивание). Temp=ArraySize(*Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo()) For i=0 To Temp 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 Next i Next ClearList(*Local_TorrentList\TorrentList()\GetPieces_List()) ; Закрытие файов торрента. SizeArray = ArraySize(*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles())-1 For i=0 To SizeArray Temp=*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileID If Temp If IsFile(Temp) CloseFile(Temp) EndIf *Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileID=0 *Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\WriteStatus=#False EndIf Next i G_TorrentList\TorrentList()\Network_IO\RemainsTime = -1 ; Время до окончания скачивания. *Local_TorrentList\TorrentList()\Endgame_Mode = #False EndProcedure Procedure Peer_File_PieceBuffer_Add(*Local_TorrentList.Core_Torrent, Piece.l, *Mem) ; Добавление новой части файла торрента в список кеша запросов от пиров. Protected TempPiece.l, *TempMem, x, Size.l TempPiece=0 *TempMem =0 If *Mem x=#False ForEach *Local_TorrentList\TorrentList()\File_PieceBuffer() If *Local_TorrentList\TorrentList()\File_PieceBuffer()\Piece = Piece ; Эта часть уже есть в списке. x=#True Break EndIf Next If x=#True ; Эта часть уже есть в списке. If ListIndex(*Local_TorrentList\TorrentList()\File_PieceBuffer())>0 TempPiece = *Local_TorrentList\TorrentList()\File_PieceBuffer()\Piece *TempMem = *Local_TorrentList\TorrentList()\File_PieceBuffer()\Point DeleteElement(*Local_TorrentList\TorrentList()\File_PieceBuffer()) EndIf Else; Нет данных об этой части. TempPiece = Piece ; *TempMem = AllocateMemory(*Local_TorrentList\TorrentList()\piece_length+2) If *TempMem CopyMemory(*Mem, *TempMem, *Local_TorrentList\TorrentList()\piece_length) EndIf EndIf If TempPiece>=0 And *TempMem ; Если часть была в списке, то перемещаем ее в его начало, а если небыло, то просто добавляем. FirstElement(*Local_TorrentList\TorrentList()\File_PieceBuffer()) If InsertElement(*Local_TorrentList\TorrentList()\File_PieceBuffer()) *Local_TorrentList\TorrentList()\File_PieceBuffer()\Piece = TempPiece *Local_TorrentList\TorrentList()\File_PieceBuffer()\Point = *TempMem Else FreeMemory(*TempMem) EndIf ElseIf *TempMem FreeMemory(*TempMem) *TempMem = 0 EndIf Size=ListSize(*Local_TorrentList\TorrentList()\File_PieceBuffer()) If *Local_TorrentList\TorrentList()\File_Max_CountList<1 *Local_TorrentList\TorrentList()\File_Max_CountList=1 EndIf If Size>*Local_TorrentList\TorrentList()\File_Max_CountList ; Размер списка больше допустимого. If LastElement(*Local_TorrentList\TorrentList()\File_PieceBuffer()) ; Самый последний элемент в списке. For x=Size To *Local_TorrentList\TorrentList()\File_Max_CountList Step -1 If *Local_TorrentList\TorrentList()\File_PieceBuffer()\Point FreeMemory(*Local_TorrentList\TorrentList()\File_PieceBuffer()\Point) *Local_TorrentList\TorrentList()\File_PieceBuffer()\Point=0 EndIf DeleteElement(*Local_TorrentList\TorrentList()\File_PieceBuffer()) Next x EndIf EndIf EndIf EndProcedure Procedure Peer_File_PieceBuffer_Get(*Local_TorrentList.Core_Torrent, Piece.l, begin.l, length.l) ; Запрос части из кеша торрента. Protected *Result, *TempMem *Result=0 ForEach *Local_TorrentList\TorrentList()\File_PieceBuffer() If *Local_TorrentList\TorrentList()\File_PieceBuffer()\Piece = Piece ; Нашли требуемую часть. If begin>=0 And length>0 And begin+length<=*Local_TorrentList\TorrentList()\piece_length *Result=AllocateMemory(length+2) If *Result *TempMem = *Local_TorrentList\TorrentList()\File_PieceBuffer()\Point CopyMemory(*TempMem+begin, *Result, length) If ListIndex(*Local_TorrentList\TorrentList()\File_PieceBuffer())>0 ; Если текущий элемент имеет индекс больше нуля, то перемещаем его в начало списка. DeleteElement(*Local_TorrentList\TorrentList()\File_PieceBuffer()) FirstElement(*Local_TorrentList\TorrentList()\File_PieceBuffer()) If InsertElement(*Local_TorrentList\TorrentList()\File_PieceBuffer()) *Local_TorrentList\TorrentList()\File_PieceBuffer()\Piece = Piece *Local_TorrentList\TorrentList()\File_PieceBuffer()\Point = *TempMem Else FreeMemory(*TempMem) EndIf EndIf Else *Result=-1 EndIf Else *Result=-1 ; Грубая ошибка в параметрах запроса части. EndIf Break EndIf Next ProcedureReturn *Result EndProcedure Procedure Peer_ReadPiece_File(*Local_TorrentList.Core_Torrent, Piece.l, begin.l, length.l) ; Чтение указаного куска из файла. Кусок читается со смещением и указанной длиной. Protected *Result, SizeArray, i, FileID Protected *Mem, MemPos, Err, File.s Protected Temp, FileSize.q, piece_length, Shift_P.q, FilePos.q Protected PieceStart.l, PieceEnd.l, ShiftStart.l, ShiftEnd.l Protected FileLen;, CountPiece Err = 0 *Mem = 0 *Result = 0 FileLen=0 If *Local_TorrentList\TorrentList()\piece_length>0 And *Local_TorrentList\TorrentList()\piece_length<=67108864 ; 64 МБ. *Result=Peer_File_PieceBuffer_Get(*Local_TorrentList.Core_Torrent, Piece, begin, length) ; Запрос части из кеша торрента. If *Result=0 ; В кеше не найдена запрашиваемая часть. MemPos = 0 piece_length = *Local_TorrentList\TorrentList()\piece_length *Mem = AllocateMemory(piece_length+2) *Result = AllocateMemory(length+2) If *Mem And *Result SizeArray = ArraySize(*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles())-1 For i=0 To SizeArray ; Ищем файл(ы) которым пренадлежит требуемый кусок. File = *Local_TorrentList\TorrentList()\Torrent\Path + *Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileName PieceStart=*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\PieceStart PieceEnd = *Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\PieceEnd ShiftStart = *Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\ShiftStart ShiftEnd = *Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\ShiftEnd FilePos=-1 : FileLen = -1 If PieceStart<=Piece And PieceEnd>=Piece ; Этот файл содержит требуемый кусок. FileID=*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileID If FileID=0 Or IsFile(FileID) = 0 FileID=0 EndIf If FileID=0 FileSize=*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\SizeFile If FileSize(File) = FileSize ; Файл имеет требуемый размер. If FileSize>0 FileID=ReadFile(#PB_Any, File) *Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileID = FileID *Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\WriteStatus=#False ; Файл открыт только для чтения. EndIf Else Err=1 Out_AddMessageTray_Balloon(#MessageName, "Ошибка торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"'."+Chr(10)+"Отсутствует или имеет некорректный размер, файл '"+*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileName+"'.", #NIIF_ERROR) AddGadgetItem(#MainWin_Panel_Info_ListIcon_Log, -1, TimeLog()+"Файл '"+*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileName+"' торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"' отсутствует или имеет некорректный размер.", ImageID(#MainWin_TorrentIcon_bag)) EndIf EndIf If Err=0 If FileID ; Файл открыт для чтения и записи. FileLen=0 ; Тут будет число байт, которые нужно прочитать из текущего файла. If PieceEnd = PieceStart ; Файл расположен в пределах одной части. FilePos = 0 FileLen=ShiftEnd-ShiftStart ; Сколько байт нужно прочитать. ElseIf PieceEnd > PieceStart ; Файл занимает болше чем одну часть. FilePos = piece_length*(Piece-PieceStart) If ShiftStart>0 If FilePos>=piece_length : FilePos-piece_length : EndIf FilePos+(piece_length-ShiftStart) EndIf If Piece = PieceStart ; Это первая часть этого файла. FilePos = 0 If ShiftStart>0 ; Кусочек предыдущей части должен быть прочитан с начала файла. FileLen = piece_length-ShiftStart Else ; Вся часть читается из текущего файла. FileLen = piece_length EndIf ElseIf Piece = PieceEnd ; Это последнаяя часть этого файла. FileLen = ShiftEnd Else ; Это не первая и не последнаяя часть файла. FileLen = piece_length EndIf Else ; Баг. Err=2 CompilerIf #Debug_Mode = #True AddGadgetItem(#MainWin_Panel_Info_ListIcon_Log, -1, TimeLog()+"Файл '"+*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileName+"' торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"' часть "+Str(Piece)+" PieceStart меньше нуля", ImageID(#MainWin_Panel_Info_Icon_Log_Warning)) CompilerEndIf Break EndIf If FileLen>0 And FilePos>=0 And Err=0 If FileLen+MemPos <= piece_length FileSeek(FileID, FilePos) Temp=ReadData(FileID, *Mem+MemPos, FileLen) MemPos+Temp Else Err=3 ; Почему-то было прочитано больше размера части Глюююк. CompilerIf #Debug_Mode = #True AddGadgetItem(#MainWin_Panel_Info_ListIcon_Log, -1, TimeLog()+"Файл '"+*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileName+"' торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"' часть "+Str(Piece)+" - было прочитано больше размера части "+Str(FileLen+MemPos), ImageID(#MainWin_Panel_Info_Icon_Log_Warning)) CompilerEndIf Break EndIf Else If Err=0 : Err=3 CompilerIf #Debug_Mode = #True Else AddGadgetItem(#MainWin_Panel_Info_ListIcon_Log, -1, TimeLog()+"Файл '"+*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileName+"' торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"' часть "+Str(Piece)+" - ошибка позиционирования (чтение) в файле или размера.", ImageID(#MainWin_Panel_Info_Icon_Log_Warning)) CompilerEndIf EndIf EndIf Else If FileSize>0 Err=1 CompilerIf #Debug_Mode = #True AddGadgetItem(#MainWin_Panel_Info_ListIcon_Log, -1, TimeLog()+"Файл '"+*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileName+"' торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"' часть "+Str(Piece)+" - не удалось открыть файл открыт для чтения и записи", ImageID(#MainWin_Panel_Info_Icon_Log_Warning)) CompilerEndIf Break EndIf EndIf Else Break EndIf EndIf Next i If Err=0 If MemPos = piece_length Or (MemPos=ShiftEnd And PieceEnd=*Local_TorrentList\TorrentList()\Torrent\CountPiece-1); Было прочитано ровно столько, сколько нужно. If ArraySize(*Local_TorrentList\TorrentList()\SHA1_pieces())>=Piece If LCase(SHA1Fingerprint(*Mem, MemPos)) = LCase(*Local_TorrentList\TorrentList()\SHA1_pieces(Piece)) ; усе ОК. CopyMemory(*Mem+begin, *Result, length) Peer_File_PieceBuffer_Add(*Local_TorrentList, Piece, *Mem) Else Err=1 Out_AddMessageTray_Balloon(#MessageName, "Ошибка торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"'."+Chr(10)+"Ошибка SHA1 в файле '"+*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileName+"'.", #NIIF_ERROR) AddGadgetItem(#MainWin_Panel_Info_ListIcon_Log, -1, TimeLog()+"В файле '"+*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileName+"' торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"' не совпала SHA1 сумма с ожидаемой!", ImageID(#MainWin_TorrentIcon_bag)) EndIf Else Err=2 CompilerIf #Debug_Mode = #True AddGadgetItem(#MainWin_Panel_Info_ListIcon_Log, -1, TimeLog()+"Файл '"+*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileName+"' торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"' часть "+Str(Piece)+" - превышение индекса массива частей", ImageID(#MainWin_Panel_Info_Icon_Log_Warning)) CompilerEndIf EndIf Else CompilerIf #Debug_Mode = #True AddGadgetItem(#MainWin_Panel_Info_ListIcon_Log, -1, TimeLog()+"Файл '"+*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileName+"' торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"' часть "+Str(Piece)+" - прочитано больше или меньше размера части "+Str(MemPos), ImageID(#MainWin_Panel_Info_Icon_Log_Warning)) CompilerEndIf Err=3 EndIf EndIf Else Err=3 EndIf If Err ; Произошла ошибка. If *Result : FreeMemory(*Result) : *Result=0 : EndIf If Err=1 Or Err=2 ; Несерьёзная ошибка, например, фай есть и имеет требуемый размер, но не получилось открыть его для чтения и записи. For i=0 To SizeArray PieceStart=*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\PieceStart PieceEnd = *Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\PieceEnd If PieceStart<=Piece And PieceEnd>=Piece ; Этот файл содержит требуемый кусок. FileID=*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileID If FileID If IsFile(FileID) CloseFile(FileID) *Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileID=0 *Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\WriteStatus=#False EndIf EndIf EndIf Next i EndIf If Err=1 ; Это серьёзная ошибка, как правило, нарешена целостность торрента. *Local_TorrentList\TorrentList()\TorrentStatus = #TorrentStatus_Error EndIf EndIf ElseIf *Result=-1 ; Ошибка запроса из кеша. *Result=0 CompilerIf #Debug_Mode = #True AddGadgetItem(#MainWin_Panel_Info_ListIcon_Log,-1, TimeLog()+"Файл '"+*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileName+"' торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"' часть"+Str(Piece)+" - Ошибка запроса из кеша.", ImageID(#MainWin_Panel_Info_Icon_Log_Warning)) CompilerEndIf EndIf EndIf If *Mem : FreeMemory(*Mem) : *Mem=0 : EndIf ProcedureReturn *Result EndProcedure Procedure Peer_WritePiece_File(*Local_TorrentList.Core_Torrent, Piece, *Point, length) ; Сохранение части торрента в файле. Protected SizeArray, i, File.s, FileID Protected PieceStart.l, PieceEnd.l, ShiftStart.l, ShiftEnd.l Protected TempQ.q, FileSize.q, Shift_P.q, FilePos.q ;x Protected FileLen, Err, piece_length, MemPos, Temp Protected *Mem, *Have Err = 0 MemPos=0 SizeArray = ArraySize(*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles())-1 piece_length = *Local_TorrentList\TorrentList()\piece_length For i=0 To SizeArray File = *Local_TorrentList\TorrentList()\Torrent\Path + *Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileName PieceStart=*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\PieceStart PieceEnd = *Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\PieceEnd ShiftStart = *Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\ShiftStart ShiftEnd = *Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\ShiftEnd FilePos = -1 : FileLen = -1 If PieceStart<=Piece And PieceEnd>=Piece ; Этот файл содержит требуемый кусок. FileID=*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileID If FileID=0 Or IsFile(FileID) = 0 FileID=0 ElseIf *Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\WriteStatus=#False ; Файл открыт только для чтения, а нам нужно и для записи тоже. CloseFile(FileID) FileID=0 *Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileID = 0 EndIf If FileID=0 FileSize=*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\SizeFile TempQ = FileSize(File) If TempQ = -1 ; Файла нет - надо создать. If CreateMoreDirectory(File)=#True ; Успешно создана папка(и), а может они уже существовали, не важно, главное что папка есть. If CreateFile_Size(File, FileSize)=#True ; Создание файла и заполнение его нулевыми байттами чтобы он имел требуемый размер. *Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\TestFile = #True ; Файл появился на диске Else ; Ошибка при создании файла. Err = 1 Out_AddMessageTray_Balloon(#MessageName, "Ошибка торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"'."+Chr(10)+"Ошибка при создании файла '"+*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileName+"'", #NIIF_ERROR) AddGadgetItem(#MainWin_Panel_Info_ListIcon_Log, -1, TimeLog()+"Ошибка торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"'. Ошибка при создании файла '"+File+"'", ImageID(#MainWin_TorrentIcon_bag)) Break EndIf Else ; Ошибка при создании папки. Err = 1 Out_AddMessageTray_Balloon(#MessageName, "Ошибка торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"'."+Chr(10)+"Ошибка при создании папки торрента!", #NIIF_ERROR) AddGadgetItem(#MainWin_Panel_Info_ListIcon_Log, -1, TimeLog()+"Ошибка торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"'. Ошибка при создании папки торрента '"+GetPathPart(File)+"'.", ImageID(#MainWin_TorrentIcon_bag)) Break EndIf ElseIf TempQ <> FileSize ; Файл имеет неправильный размер. Goto Peer_WritePiece_File_m1 ; Нет смысла два раз дублировать одно и тоже. EndIf If Err = 0 TempQ = FileSize(File) If TempQ = FileSize ; Так, ОК, с размером все нормально. FileID=OpenFile(#PB_Any, File) ; Открываем файл. *Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileID = FileID *Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\WriteStatus=#True ; Файл открыт для чтения и записи. Else Peer_WritePiece_File_m1: Err = 1 Out_AddMessageTray_Balloon(#MessageName, "Ошибка торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"'."+Chr(10)+"Ошибка размера файла '"+*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileName+"'", #NIIF_ERROR) AddGadgetItem(#MainWin_Panel_Info_ListIcon_Log, -1, TimeLog()+"Ошибка торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"'. Ошибка размера файла '"+File+"' На диске "+ConvertByte_ToString(TempQ, 2)+"Должно быть "+ConvertByte_ToString(FileSize, 2), ImageID(#MainWin_TorrentIcon_bag)) Break EndIf EndIf EndIf If Err = 0 If FileID FileLen=0 ; Тут будет число байт, которые нужно прочитать из текущего файла. If PieceEnd = PieceStart ; Файл расположен в пределах одной части. FilePos = 0 FileLen=ShiftEnd-ShiftStart ; Сколько байт нужно записать. ElseIf PieceEnd > PieceStart ; Файл занимает болше чем одну часть. FilePos = piece_length*(Piece-PieceStart) If ShiftStart>0 If FilePos>=piece_length : FilePos-piece_length : EndIf FilePos+(piece_length-ShiftStart) EndIf If Piece = PieceStart ; Это первая часть этого файла. FilePos = 0 If ShiftStart>0 ; Кусочек предыдущей части должен быть записан в начало файла. FileLen = piece_length-ShiftStart Else ; Вся часть пишется в текущий файл. FileLen = piece_length EndIf ElseIf Piece = PieceEnd ; Это последнаяя часть этого файла., FileLen = ShiftEnd Else ; Это не первая и не последнаяя часть файла. FileLen = piece_length EndIf Else ; Баг. Err=2 CompilerIf #Debug_Mode = #True AddGadgetItem(#MainWin_Panel_Info_ListIcon_Log, -1, TimeLog()+"Файл '"+*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileName+"' торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"' часть"+Str(Piece)+" PieceStart меньше нуля", ImageID(#MainWin_Panel_Info_Icon_Log_Warning)) CompilerEndIf Break EndIf If FileLen>0 And FilePos>=0 And Err=0 If FileLen+MemPos <= piece_length FileSeek(FileID, FilePos) Temp=WriteData(FileID, *Point+MemPos, FileLen) FlushFileBuffers(FileID) *Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\RealFileSize + Temp MemPos+Temp Else Err=3 ; Почему-то было записано больше размера части Глюююк. CompilerIf #Debug_Mode = #True AddGadgetItem(#MainWin_Panel_Info_ListIcon_Log, -1, TimeLog()+"Файл '"+*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileName+"' торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"' часть"+Str(Piece)+" - было прочитано больше размера части "+Str(FileLen+MemPos), ImageID(#MainWin_Panel_Info_Icon_Log_Warning)) CompilerEndIf Break EndIf Else If Err=0 : Err=3 CompilerIf #Debug_Mode = #True Else AddGadgetItem(#MainWin_Panel_Info_ListIcon_Log, -1, TimeLog()+"Файл '"+*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileName+"' торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"' часть"+Str(Piece)+" - ошибка позиционирования (запись) в файле или размера.", ImageID(#MainWin_Panel_Info_Icon_Log_Warning)) CompilerEndIf EndIf EndIf Else Err=1 AddGadgetItem(#MainWin_Panel_Info_ListIcon_Log, -1, TimeLog()+"Файл '"+*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileName+"' торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"' - не удалось открыть файл открыт для чтения и записи", ImageID(#MainWin_Panel_Info_Icon_Log_Warning)) Break EndIf EndIf EndIf Next i If Err ; Произошла ошибка. If Err=1 Or Err=2 ; Несерьёзная ошибка, например, фай есть и имеет требуемый размер, но не получилось открыть его для чтения и записи. For i=0 To SizeArray PieceStart=*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\PieceStart PieceEnd = *Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\PieceEnd If PieceStart<=Piece And PieceEnd>=Piece ; Этот файл содержит требуемый кусок. FileID=*Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileID If FileID If IsFile(FileID) CloseFile(FileID) *Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\FileID=0 *Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\WriteStatus=#False EndIf EndIf EndIf Next i EndIf If Err=1 ; Это серьёзная ошибка, как правило, нарешена целостность торрента. *Local_TorrentList\TorrentList()\TorrentStatus = #TorrentStatus_Error EndIf Else ; Ошибок нет и часть была записана на диск. Torrent_SetArray_MapPiece(*Local_TorrentList\TorrentList()\Torrent\MapFile\MapPiece(), Piece, 1) ; Отмечаем что кусок есть на диске. *Local_TorrentList\TorrentList()\Torrent\CurrentCountPiece = Torrent_CountPiece(*Local_TorrentList\TorrentList()\Torrent\MapFile\MapPiece(), *Local_TorrentList\TorrentList()\Torrent\CountPiece) *Local_TorrentList\TorrentList()\Torrent\CurrentFileSize + MemPos ; Сколько байт есть на диске этого торрента. ; Информируем подключенных пиров что часть была скачана. *Have=Peer_Create_Have(Piece) If *Have ForEach *Local_TorrentList\TorrentList()\Network\PeerList() If *Local_TorrentList\TorrentList()\Network\PeerList()\ConnectID *Mem=AllocateMemory(10) If *Mem CopyMemory(*Have, *Mem, 9) Peer_AddList_OutData(*Local_TorrentList, *Mem, 9) EndIf EndIf Next FreeMemory(*Have) EndIf ; Скачан ли полностью торррент. If Torrent_CountPiece(*Local_TorrentList\TorrentList()\Torrent\MapFile\MapPiece(), *Local_TorrentList\TorrentList()\Torrent\CountPiece) >= *Local_TorrentList\TorrentList()\Torrent\CountPiece ; Торрент скачан. *Local_TorrentList\TorrentList()\TorrentStatus = #TorrentStatus_Seed ; Переход в режим сидитирования. ; Список запрашиваемых частей нужно обнулить. ForEach *Local_TorrentList\TorrentList()\GetPieces_List() Temp=ArraySize(*Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo()) For i=0 To Temp 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 If *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\ConnectID If *Local_TorrentList\TorrentList()\Current_CountBlockDownload>0 *Local_TorrentList\TorrentList()\Current_CountBlockDownload - 1 EndIf *Local_TorrentList\TorrentList()\Network\PeerList()\CurrentRequest - 1 EndIf Next i Next Out_AddMessageTray_Balloon(#MessageName, "Скачан торрент '"+*Local_TorrentList\TorrentList()\TorrentName+"'", #NIIF_INFO) EndIf EndIf ProcedureReturn Err EndProcedure ; В списке должен быть выбран текущий торрент и текущая часть в *Local_TorrentList\TorrentList()\GetPieces_List() Procedure Peer_GetPiecesList_TestPiece(*Local_TorrentList.Core_Torrent) ; Процедура вызывается после приема очередного блока и проверяет принята ли вся часть. Protected SizeArray, i, x, piece_length Protected *Point, MemPos.l, Temp x = #False SizeArray = ArraySize(*Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo()) If SizeArray>=0 x = #True For i=0 To SizeArray If *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\Point = 0 ; Этого блока пока нет. x = #False Break EndIf Next i EndIf If x = #True ; Похоже что все блоки загружены и часть получена. x = #False MemPos = 0 piece_length = *Local_TorrentList\TorrentList()\piece_length ; Размер части торрента. *Point = AllocateMemory(piece_length) If *Point For i=0 To SizeArray CopyMemory(*Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\Point, *Point + *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\begin, *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\length) MemPos + *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\length FreeMemory(*Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\Point) *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 If MemPos=piece_length Or (MemPos *Local_TorrentList\TorrentList()\GetPieces_List()\Piece If LCase(SHA1Fingerprint(*Point, MemPos)) = LCase(*Local_TorrentList\TorrentList()\SHA1_pieces(*Local_TorrentList\TorrentList()\GetPieces_List()\Piece)) Peer_WritePiece_File(*Local_TorrentList, *Local_TorrentList\TorrentList()\GetPieces_List()\Piece, *Point, MemPos) ; Сохранение части торрента в файле. ; Часть скачана и ее нужно удалить из списка скачиваемых блоков. For i=0 To SizeArray;Temp 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 *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\ConnectID=0 Next i DeleteElement(*Local_TorrentList\TorrentList()\GetPieces_List()) Else ; Это серьезная ошибка. *Local_TorrentList\TorrentList()\Network_IO\Excess + MemPos ;Out_AddMessageTray_Balloon(#MessageName, "Ошибка торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"'."+Chr(10)+"При приеме части "+Str(*Local_TorrentList\TorrentList()\GetPieces_List()\Piece)+" не совпал SHA1 хеш с ожидаемым!", #NIIF_ERROR) AddGadgetItem(#MainWin_Panel_Info_ListIcon_Log, -1, TimeLog()+"При приеме части "+Str(*Local_TorrentList\TorrentList()\GetPieces_List()\Piece)+" торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"' произошла ошибка: - не совпал SHA1 хеш с ожидаемым!", ImageID(#MainWin_Panel_Info_Icon_Log_Warning)) EndIf Else *Local_TorrentList\TorrentList()\Network_IO\Excess + MemPos CompilerIf #Debug_Mode = #True AddGadgetItem(#MainWin_Panel_Info_ListIcon_Log, -1, TimeLog()+"При приеме части "+Str(*Local_TorrentList\TorrentList()\GetPieces_List()\Piece)+" торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"' произошла ошибка: - превышение индекса массива частей", ImageID(#MainWin_Panel_Info_Icon_Log_Warning)) CompilerEndIf EndIf Else *Local_TorrentList\TorrentList()\Network_IO\Excess + MemPos CompilerIf #Debug_Mode = #True AddGadgetItem(#MainWin_Panel_Info_ListIcon_Log, -1, TimeLog()+"При приеме части "+Str(*Local_TorrentList\TorrentList()\GetPieces_List()\Piece)+" торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"' произошла ошибка. Некорректный размер части. Принято "+Str(MemPos)+" а должно быть "+Str(piece_length), ImageID(#MainWin_Panel_Info_Icon_Log_Warning)) CompilerEndIf EndIf FreeMemory(*Point) EndIf EndIf EndProcedure ; Procedure.a Peer_InCommand_Have(*Local_TorrentList.Core_Torrent, *Point, Size) ; Have-сообщение фиксированной длины. Полезная нагрузка - это с указанием нулей (zero-based) индекс куска, который только что был успешно скачан и проверен с помощью хэша. Protected Result.a, PieceIndex If Size>=5+4 Result = #True ; Отмечаем что сообщение обработано. PieceIndex = ReverseLong(PeekL(*Point+5)) If *Local_TorrentList\TorrentList()\Torrent\CountPiece>=PieceIndex Torrent_SetArray_MapPiece(*Local_TorrentList\TorrentList()\Network\PeerList()\MapPiece(), PieceIndex, 1) ; Отмечаем что у пира есть этот кусок. *Local_TorrentList\TorrentList()\Network\PeerList()\CountHave + 1 ; Подсчет Have сообщений для вычисления скорости пира. EndIf Else Result = #False ; Мало данных. EndIf ProcedureReturn Result EndProcedure ; Procedure Peer_InCommand_BitField(*Local_TorrentList.Core_Torrent, *Point, Size, MessageLen) ; Битовое поле в котором каждый бит, обозначает есть ли кусок у пира: 1- есть: 0 - куска нет. Protected Result = #False, i, Byte.a;, Count If Size >= MessageLen+4 Result = #True ; Отмечаем что сообщение обработано. MessageLen-1 If MessageLen = ArraySize(*Local_TorrentList\TorrentList()\Network\PeerList()\MapPiece()) ; Требуемый размер данных. MessageLen-1 For i=0 To MessageLen Byte = ReverseBits_Byte(PeekA(*Point+i+5)) *Local_TorrentList\TorrentList()\Network\PeerList()\MapPiece(i) = Byte Next i i = *Local_TorrentList\TorrentList()\Torrent\CountPiece % 8 If i>0 And i<8 Byte>>i If Byte ; Тут должет быть ноль. Если нет, то разорвать связь с пиром. Result = -1 EndIf EndIf Else ; Длина сообщения не равна числу частей - баг, пира в диктонект! Result = -1 CompilerIf #Debug_Mode = #True AddGadgetItem(#MainWin_Panel_Info_ListIcon_Log,-1, TimeLog()+"Длина 'BitField' имеет некорректный размер. Торрент '"+*Local_TorrentList\TorrentList()\TorrentName+"'", ImageID(#MainWin_Panel_Info_Icon_Log_Warning)) CompilerEndIf EndIf Else Result = #False ; Мало данных. EndIf ProcedureReturn Result EndProcedure ; Procedure Peer_InCommand_Request(*Local_TorrentList.Core_Torrent, *Point, Size) ; Обработка запроса пира, требуемого куска. Короче, пир попросил у нас блок кукска. Protected Result = #False Protected index.l, begin.l, length.l, *MemBlock If Size >= 17 *Local_TorrentList\TorrentList()\Network\PeerList()\CountIn_Request + 1 ; Для отображения числа запросов в таблице. Result = #True ; Отмечаем что сообщение обработано. index = ReverseLong(PeekL(*Point+5)) ; Индекс запрашиваемого куска, начиная с нуля. begin = ReverseLong(PeekL(*Point+9)) ; Смещение байтов внутри куска. length= ReverseLong(PeekL(*Point+13)) ; Сколько байт нужно передать. ; Мы не блокируем пира и пир заинтерисован в нас. If *Local_TorrentList\TorrentList()\Network\PeerList()\MyStatus\Choke=#False And *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_Status\Interested=#True If index>=0 And index<*Local_TorrentList\TorrentList()\Torrent\CountPiece If begin>=0 And length>0 And begin+length<=*Local_TorrentList\TorrentList()\piece_length If Torrent_GetArray_MapPiece(*Local_TorrentList\TorrentList()\Torrent\MapFile\MapPiece(), index) ; А есть ли у нас требуемый кусок? If length<=128*1024 *MemBlock= Peer_ReadPiece_File(*Local_TorrentList, index, begin, length) If *MemBlock ; Успешно прочитали с диска требуемый блок. Peer_Send_Piece(*Local_TorrentList.Core_Torrent, index, begin, length, *MemBlock) ; Отправка пиру запрошеного блока. FreeMemory(*MemBlock) EndIf Else ; Согласно спецификации, если запрошен блок, размером больше 128 КБ, то нужно разорвать соединение с пиром. Result = -1 CompilerIf #Debug_Mode = #True AddGadgetItem(#MainWin_Panel_Info_ListIcon_Log,-1, TimeLog()+"Был получен блок, размром больше 128 КБ, части "+Str(index)+" торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"'", ImageID(#MainWin_Panel_Info_Icon_Log_Warning)) CompilerEndIf EndIf Else ; Куска нет - пира к дисконект! Result = -1 CompilerIf #Debug_Mode = #True AddGadgetItem(#MainWin_Panel_Info_ListIcon_Log,-1, TimeLog()+"Пир запросил несуществующую у нас часть "+Str(index)+" торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"'", ImageID(#MainWin_Panel_Info_Icon_Log_Warning)) CompilerEndIf EndIf Else ; Неккоектные данные или выход за пределы части. Result = -1 CompilerIf #Debug_Mode = #True AddGadgetItem(#MainWin_Panel_Info_ListIcon_Log,-1, TimeLog()+"Некорректные данные или выход за пределы части "+Str(index)+" торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"'", ImageID(#MainWin_Panel_Info_Icon_Log_Warning)) CompilerEndIf EndIf Else ; Указан некорректный индекс части. Result = -1 CompilerIf #Debug_Mode = #True AddGadgetItem(#MainWin_Panel_Info_ListIcon_Log,-1, TimeLog()+"Указан некорректный индекс части "+Str(index)+" торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"'", ImageID(#MainWin_Panel_Info_Icon_Log_Warning)) CompilerEndIf EndIf EndIf Else Result = #False ; Мало данных. EndIf ProcedureReturn Result EndProcedure ; Procedure Peer_InCommand_Piece(*Local_TorrentList.Core_Torrent, *Point, Len, MessageLen) ; Пир прислал запрошенный блок, требуемой нам части. Protected Result = #False Protected index.l, begin.l, length.l, *MemBlock Protected Temp, i, ConnectID;, zz, xx Protected Ok_State.a, EndgameMode.a Ok_State=0 ;Debug Str(ReverseLong(PeekL(*Point+5)))+" "+Str(ReverseLong(PeekL(*Point+9))) If Len >= MessageLen+4 Result = #True ; Отмечаем что сообщение обработано. index = ReverseLong(PeekL(*Point+5)) ; Индекс куска, начиная с нуля. begin = ReverseLong(PeekL(*Point+9)) ; Смещение байтов внутри куска. length=MessageLen-9 If length<=#Peer_PieceLength ;Or (length<#Peer_PieceLength And index=*Local_TorrentList\TorrentList()\Torrent\CountPiece-1) ConnectID = *Local_TorrentList\TorrentList()\Network\PeerList()\ConnectID ForEach *Local_TorrentList\TorrentList()\GetPieces_List() ; Смотрим, есть ли данные части в списке загрузок. If *Local_TorrentList\TorrentList()\GetPieces_List()\Piece = index ; Нашли часть в списке текущих загрузок этого торрента *Local_TorrentList\TorrentList()\GetPieces_List()\Time = *Local_TorrentList\Info\Current_Time ; Время запроса части. Необходимо для того, чтобы сбросить по таймауту прием части, котой нет у текущих пиров. Temp=ArraySize(*Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo()) For i=0 To Temp If *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\begin = begin If *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\length = length Ok_State=1 ; Ответ получен от того пира, которому был отправлен запрос. EndgameMode = Peer_EndgameMode_GetPiece(*Local_TorrentList, i) If *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\ConnectID = ConnectID Or EndgameMode=#True If *Local_TorrentList\TorrentList()\Current_CountBlockDownload>0 *Local_TorrentList\TorrentList()\Current_CountBlockDownload-1 ; Текущее число запросов блоков для этого торранта. EndIf *Local_TorrentList\TorrentList()\Network\PeerList()\CurrentRequest-1 ; Текущее число запросов блоков для пира. If *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\Point = 0 *MemBlock = AllocateMemory(length+2) If *MemBlock CopyMemory(*Point+13, *MemBlock, length) *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\Point = *MemBlock Peer_GetPiecesList_TestPiece(*Local_TorrentList) ; Процедура вызывается после приема очередного блока и процеряет принатя ли вся часть. EndIf Else *Local_TorrentList\TorrentList()\Network_IO\Excess + length CompilerIf #Debug_Mode = #True AddGadgetItem(#MainWin_Panel_Info_ListIcon_Log,-1, TimeLog()+"Был повторно получен блок части "+Str(index)+" торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"'", ImageID(#MainWin_Panel_Info_Icon_Log_Warning)) CompilerEndIf EndIf EndIf Break EndIf EndIf Next i Break EndIf Next If Ok_State=0 ;Result = -1 If length>0 *Local_TorrentList\TorrentList()\Network_IO\Excess + length EndIf CompilerIf #Debug_Mode = #True AddGadgetItem(#MainWin_Panel_Info_ListIcon_Log,-1, TimeLog()+"При приеме части "+Str(index)+" торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"' произошла ошибка. Пир (конект "+Str(ConnectID)+") блок прислал, но параметры неверные. Часть "+Str(index)+" смещение "+Str(begin)+" длина "+Str(length), ImageID(#MainWin_Panel_Info_Icon_Log_Warning)) CompilerEndIf EndIf Else ; Размер блока не равен запрашиемому - пира в топку. Result = -1 If length>0 *Local_TorrentList\TorrentList()\Network_IO\Excess + length EndIf CompilerIf #Debug_Mode = #True AddGadgetItem(#MainWin_Panel_Info_ListIcon_Log,-1, TimeLog()+"При приеме части "+Str(index)+" торрента '"+*Local_TorrentList\TorrentList()\TorrentName+"' произошла ошибка. Некорректный размер блока.", ImageID(#MainWin_Panel_Info_Icon_Log_Warning)) CompilerEndIf EndIf Else Result = #False ; Мало данных. EndIf ProcedureReturn Result EndProcedure ; cancel: Procedure Peer_InCommand_Cancel(*Local_TorrentList.Core_Torrent, *Point, Len) ; Пир отменил Request-запрос - нужно изъять из очереди подготовленный блок для пира. Protected Result = #False Protected *Mem If Len>=17 Result = #True ; Отмечаем что сообщение обработано. If ListSize(*Local_TorrentList\TorrentList()\Network\PeerList()\QueueList())>0 ; Список передачи не пустой. If *Local_TorrentList\TorrentList()\Network\PeerList()\PosQueue=0 ; Данные еще не отправлялись с нулевого элемента списка ResetList(*Local_TorrentList\TorrentList()\Network\PeerList()\QueueList()) Else FirstElement(*Local_TorrentList\TorrentList()\Network\PeerList()\QueueList()) EndIf While NextElement(*Local_TorrentList\TorrentList()\Network\PeerList()\QueueList()) *Mem = *Local_TorrentList\TorrentList()\Network\PeerList()\QueueList()\Point If *Mem If PeekL(*Mem)=$0D000000 If PeekA(*Mem+4) = 6 ; Request If PeekL(*Mem+5)=PeekL(*Point+5) And PeekL(*Mem+9)=PeekL(*Point+9) And PeekL(*Mem+13)=PeekL(*Point+13) FreeMemory(*Mem) DeleteElement(*Local_TorrentList\TorrentList()\Network\PeerList()\QueueList()) EndIf EndIf EndIf EndIf Wend EndIf Else Result = #False ; Мало данных. EndIf ProcedureReturn Result EndProcedure Procedure Peer_Comand_SetActivePeer(*Local_TorrentList.Core_Torrent) ; Активирует или диактивирует пира (визуальное отображение в таблице). If *Local_TorrentList\TorrentList()\Network\PeerList()\MyStatus\Interested = #True Or *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_Status\Interested = #True ; То ли мы заинтерисованы в пире, то ли он в нас. *Local_TorrentList\TorrentList()\Network\PeerList()\Active = #True Else ; Ни мы, ни пир не заинтерисованы в друг друге. *Local_TorrentList\TorrentList()\Network\PeerList()\Active = #False EndIf EndProcedure Procedure.a Peer_Comand_Interpretator(ClientID, *Buffer, Size, *Local_TorrentList.Core_Torrent) ; Обработка входящих соединений сервера для уже установленых коннектов. Protected *Point, Len, Temp;, Pos Protected Message.a, MessageLen, DelLen, Err.a Protected Status, Result.a Result = #False Err = #False Status = *Local_TorrentList\TorrentList()\TorrentStatus ; Статус торрента. If Status = #TorrentStatus_Load Or Status = #TorrentStatus_Seed Or Status = #TorrentStatus_Pause Peer_AddInMem(*Local_TorrentList, *Buffer, Size) ; Добавили данные в память принимаемых данных от пира. Repeat *Point = *Local_TorrentList\TorrentList()\Network\PeerList()\ReceivePoint ;Pos = *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Pos Len = *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Pos ;*Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Info_Len If Len>*Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Len Temp=*Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Len Peer_ReallocateMemory(*Local_TorrentList, Len - *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Len) Len=Temp EndIf DelLen=0 If Len>=4 ; Минимальный размер для сообщения. Temp = PeekL(*Point) MessageLen = ReverseLong(Temp) If MessageLen = 0 ; Это "keep-alive" сообщение. Peer_DelInMem(*Local_TorrentList, 4) ; Данные обработаны и их надо убрать из очереди. ElseIf MessageLen<0 Or MessageLen>=262144 ; 256 КБ. Ошибка - пира к корзину! Peer_ClearPeer(*Local_TorrentList) ; Деактивация текущего пира. В списке должен быть выбран требуемый пир. Break ElseIf Len>4 ; Передано информационное сообщение. DelLen=0 Message = PeekA(*Point+4) Select Message Case 0 ; choke - Пир блокирует клиента. ; If MessageLen=1 *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_Status\Choke = #True DelLen=4+MessageLen Else Err = #True EndIf Case 1 ; unchoke - Пир НЕ блокирует клиента (разблокировал). ; If MessageLen=1 *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_Status\Choke = #False DelLen=4+MessageLen Else Err = #True EndIf Case 2 ; interested - Пир заинтерисован в клиенте. ; If MessageLen=1 *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_Status\Interested = #True Peer_Comand_SetActivePeer(*Local_TorrentList) ; Активирует или диактивирует пира (визуальное отображение в таблице). Peer_Upload_New(*Local_TorrentList) ; Вызывается когда пир сообщает что он заинтерисован в нас DelLen=4+MessageLen Else Err = #True EndIf Case 3 ; not interested - Пир НЕ заинтерисован в клиенте. ; If MessageLen=1 *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_Status\Interested = #False *Local_TorrentList\TorrentList()\Network\PeerList()\MyStatus\Choke = #True Peer_SendMessage(*Local_TorrentList, #Peer_Message_Choke) Peer_Comand_SetActivePeer(*Local_TorrentList) ; Активирует или диактивирует пира (визуальное отображение в таблице). DelLen=4+MessageLen Else Err = #True EndIf Case 4 ; have - Это одно число (4 байта в big-endian формате), соответствующее индексу фрагмента, скачивание которого агент полностью завершил и проверил его соответствие хэшу. ; If MessageLen=5 If Peer_InCommand_Have(*Local_TorrentList, *Point, Len)=#True ; Успешная обработка компанды - нужно удалить ее данные из очереди. DelLen=4+MessageLen Else DelLen=-1 EndIf Else Err = #True EndIf Case 5 ; bitfield - Отправляется обычно при первом запросе после "Рукопожатия" и содержит битовое поле, в котором каждый бит, соответствующий индексу фрагмента, отправленного скачивающим агентом, равен единице, а все остальные биты - нулю. ; Temp = Peer_InCommand_BitField(*Local_TorrentList, *Point, Len, MessageLen) If Temp=#False ; Недостаточно данных. DelLen=-1 ElseIf Temp=-1 ; Ошибка в данных. Err = #True Else ; Успешно отрабортана команда. DelLen=4+MessageLen Temp = *Local_TorrentList\TorrentList()\TorrentStatus If Temp = #TorrentStatus_Load If *Local_TorrentList\TorrentList()\Network\PeerList()\Connect_Status = #Peer_InConnect ; Это входящее соединение. Peer_Download_InPeers(*Local_TorrentList) ; Эта процедура вызывается после того как пир, установивший входящее соединение, пришлет BitField сообщение. EndIf ElseIf Temp = #TorrentStatus_Seed Temp = *Local_TorrentList\TorrentList()\Torrent\CountPiece ; У клиента есть весь торрент и поэтому с ним нужно разорвать соединение. If Torrent_CountPiece(*Local_TorrentList\TorrentList()\Network\PeerList()\MapPiece(), Temp)>=Temp Peer_ClearPeer(*Local_TorrentList) Break EndIf EndIf EndIf Case 6 ; request - Сообщение-запрос фиксированной длины, используется для запроса блока. Он содержит индекс фрагмента, его позиционное смещение относительно начала и длину. ; If Status <> #TorrentStatus_Pause And G_ProgramMiscInfo\Torrent_All_Pause<>1 ; Если торрент на паузе, информационные сообщения запрещены. If MessageLen=13 Temp = Peer_InCommand_Request(*Local_TorrentList, *Point, Len) If Temp=#False ; Недостаточно данных. DelLen=-1 ElseIf Temp=-1 ; Ошибка в данных. Err = #True Else ; Успешно отрабортана команда. DelLen=4+MessageLen EndIf Else Err = #True EndIf Else DelLen=4+MessageLen EndIf Result = #True Case 7 ; piece - .Это наверное пир присылает запрашиваемый кусок. В доках мутно описан этот момент. ; Temp = Peer_InCommand_Piece(*Local_TorrentList, *Point, Len, MessageLen) If Temp=#False ; Недостаточно данных. DelLen=-1 ElseIf Temp=-1 ; Ошибка в данных. Err = #True Else ; Успешно отрабортана команда. DelLen=4+MessageLen *Local_TorrentList\TorrentList()\Network\PeerList()\InSpeed + MessageLen EndIf Result = #True If *Local_TorrentList\TorrentList()\TorrentStatus = #TorrentStatus_Error Break EndIf Case 8 ; cancel - .Cancel-сообщение фиксированной длины, используется для отмены блокировки запросов. Полезная нагрузка сообщения идентична той, которая была в "сообщении-запросе" ("request" message). Сообщение обычно используется во время стратегии "Конца игры" (End game, см. ниже раздел Алгоритмы). ; If MessageLen=13 Temp = Peer_InCommand_Cancel(*Local_TorrentList, *Point, Len) If Temp=#False ; Недостаточно данных. DelLen=-1 ElseIf Temp=-1 ; Ошибка в данных. Err = #True Else ; Успешно отрабортана команда. DelLen=4+MessageLen EndIf Else Err = #True EndIf ;Case 9 ; port - Порт для прослушивания является портом который DHT узел прослушивает. ; Default ; Неизвестная команда и хз как ее обрабатывать. If Len>=MessageLen+4 DelLen=4+MessageLen Else DelLen=-1 ; Недостаточно данных. EndIf EndSelect If *Local_TorrentList\TorrentList()\Network\PeerList()\Connect_Status = #Peer_InConnect ; Это входящее соединение. *Local_TorrentList\TorrentList()\Network\PeerList()\TimeOut = 0 EndIf *Local_TorrentList\TorrentList()\Network\PeerList()\InDataTime = 0 If Err = #True ; Произошла ошибка в очереди, нужно закрыть соединение с пиром. Peer_ClearPeer(*Local_TorrentList) Break Else If DelLen>0 Peer_DelInMem(*Local_TorrentList, DelLen) ; Если команда успешно обработана, то нужно удалить ее данные из очереди сообщений. ElseIf DelLen=-1 ; Передано не все сообщение - добавить в очередь. Break EndIf EndIf Else ; Передано не все сообщение - добавить в очередь. If Len<=262144 ; 256 КБ. Else ; Ошибка - пира к корзину! Peer_ClearPeer(*Local_TorrentList) EndIf Break EndIf ElseIf Len>0 ; Передано не все сообщение - добавить в очередь. If Len<=262144 ; 256 КБ. Else ; Ошибка - пира к корзину! Peer_ClearPeer(*Local_TorrentList) EndIf Break Else Break EndIf ForEver Else ; Статус торрента не позволяет работать с пирами Peer_ClearPeer(*Local_TorrentList) EndIf ProcedureReturn Result EndProcedure Procedure Peer_Server_NewConnect_Interpretator(ClientID, *Buffer, Size, *Local_TorrentList.Core_Torrent) ; Обработка входящих соединений сервера для новых коннектов. Protected Info.Peer_Handshake, x, IP.l, i, Peer_id.s Protected Status x = #False If Peer_In_Handshake(*Buffer, Size, @Info) = #True ; "Рукопожатие" не содержит ошибок. ForEach *Local_TorrentList\TorrentList() ; Ищем торрент, который запросил удаленный пир. If CompareMemory(@*Local_TorrentList\TorrentList()\INFO_HashBin[0], @Info\INFO_Hash[0], 20) = 1 ; Нашли торрент. Status = *Local_TorrentList\TorrentList()\TorrentStatus ; Статус торрента. If Status = #TorrentStatus_Load Or Status = #TorrentStatus_Seed ; Для добавления нового пира, торрент должен скачиватся или раздаваться. Peer_id = PeekS(@Info\Client, 20, #PB_Ascii) If Peer_id IP = GetClientIP(ClientID) i=#False ForEach *Local_TorrentList\TorrentList()\Network\PeerList() If *Local_TorrentList\TorrentList()\Network\PeerList()\ip = IP ; В списке есть клиент с таким IP адресом. i=#True Break EndIf Next If i=#False ; В списке нет клиента с таким IP. If AddElement(*Local_TorrentList\TorrentList()\Network\PeerList()) ; Добавили нового пира в список. *Local_TorrentList\TorrentList()\Network\PeerList()\NumTracker = -1 ; Тркер неизвестен, поэтому никакому трекеру не нужно передавать инфу об скачивании. *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_Status\Choke=#True *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_Status\Interested=#False *Local_TorrentList\TorrentList()\Network\PeerList()\MyStatus\Choke=#True *Local_TorrentList\TorrentList()\Network\PeerList()\MyStatus\Interested=#False *Local_TorrentList\TorrentList()\Network\PeerList()\PosQueue = 0 x=ArraySize(*Local_TorrentList\TorrentList()\Torrent\MapFile\MapPiece()) If x<1 : x=0 : EndIf ReDim *Local_TorrentList\TorrentList()\Network\PeerList()\MapPiece(x) For i=0 To x *Local_TorrentList\TorrentList()\Network\PeerList()\MapPiece(i) = 0 ; Отмечаем что у пира нет чатей торрента. Далее have и bitfield собщения заполнят этот массив. Next i x=0 For i=1 To 10 *Local_TorrentList\TorrentList()\Network\PeerList()\ReceivePoint = AllocateMemory(128) If *Local_TorrentList\TorrentList()\Network\PeerList()\ReceivePoint x=128 Break Else Delay(2) EndIf Next i *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Pos=0 *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Info_Len=0 *Local_TorrentList\TorrentList()\Network\PeerList()\Receive_Len=x *Local_TorrentList\TorrentList()\Network\PeerList()\ConnectID=0 x = 0 i=#True EndIf EndIf If i=#True *Local_TorrentList\TorrentList()\Network\PeerList()\InCon=#True ; Если #True, то пир установил входящее соединение. If *Local_TorrentList\TorrentList()\Network\PeerList()\ConnectID ; С этим пиром уже установлена связь! If *Local_TorrentList\TorrentList()\Network\PeerList()\Connect_Status = #Peer_InConnect Peer_ClearPeer(*Local_TorrentList) Goto Peer_Server_NewConnect_Interpretator_M1 Else x = #False EndIf Else Peer_Server_NewConnect_Interpretator_M1: *Local_TorrentList\TorrentList()\Network\PeerList()\ConnectID=ClientID *Local_TorrentList\TorrentList()\Network\PeerList()\Connect_Status = #Peer_InConnect ; Это входящее соединение. *Local_TorrentList\TorrentList()\Network\PeerList()\ip = IP *Local_TorrentList\TorrentList()\Network\PeerList()\IP$ = IPString(IP) *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_id = Peer_id *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_ConnectStatus = #True ; Успешный конект. *Local_TorrentList\TorrentList()\Network\PeerList()\Err_ConnectCount=0 *Local_TorrentList\TorrentList()\Network\PeerList()\PeerDown = #False *Local_TorrentList\TorrentList()\Network\PeerList()\PeerUp = #False *Local_TorrentList\TorrentList()\Network\PeerList()\CountIn_Request = 0 *Local_TorrentList\TorrentList()\Network\PeerList()\Time_Connect = 0 *Local_TorrentList\Info\Current_CountConnect + 1 ; Общее число соединений. *Local_TorrentList\TorrentList()\Network\CountActivePeer+1 ; Общее число пиров текущего торрента, с которыми установлен коннект. *Local_TorrentList\TorrentList()\Network\PeerList()\Time_UploadNoConnect = 0 *Local_TorrentList\TorrentList()\Network\PeerList()\MaxRequest = #Peer_MaxPeerRequest / 2 x = #True EndIf EndIf Break EndIf Else ; Статус торрента не позволяет добалять новых пиров. x = #False ;Break EndIf Break EndIf Next EndIf If x = #False ; Ошибка Handshake либо не не нашли торрент,а может статус торрента не позволяет подключать пиров - закрыть соединение. CloseNetworkConnection(ClientID) ; Пира в топку! ElseIf x = #True Peer_In_SendStartInfo(*Local_TorrentList) ; Эта процедура вызывается для генерации "Рукопожатия" и списка имеющихся частей для пира, установившего входяще соединение. If Size>68 Peer_Comand_Interpretator(ClientID, *Buffer+68, Size-68, *Local_TorrentList) ; Обработка входящих соединений сервера для уже установленых коннектов. EndIf EndIf EndProcedure Procedure.a Peer_TestInConnect(*Local_TorrentList.Core_Torrent, ClientID) ; Проверка можно ли принять входящее соединение от подключившегося пира Protected Result.a Result=#False If *Local_TorrentList\Info\Current_CountConnect <= G_ProgramSetting\Lan\MaxConnect ; Не превышено максимально допустимое число соединений. If ListIndex(*Local_TorrentList\TorrentList())<0 Result=#False ;#True ElseIf *Local_TorrentList\TorrentList()\Network\CountActivePeer < G_ProgramSetting\Lan\MaxPeer ; Максимально допустимое число пиров на торрент. Result=#True EndIf EndIf ProcedureReturn Result EndProcedure Procedure Peer_ServerCore(ClientID, *Buffer, Size, *Local_TorrentList.Core_Torrent) ; Обработка данных сервера. Protected x ; Попытаемся найти ClientID среди открытых соединений. x=#False ForEach *Local_TorrentList\TorrentList() ForEach *Local_TorrentList\TorrentList()\Network\PeerList() If *Local_TorrentList\TorrentList()\Network\PeerList()\ConnectID = ClientID ; Нашли такой идентификатор открытого соединения в списке. If *Local_TorrentList\TorrentList()\Network\PeerList()\Connect_Status = #Peer_InConnect ; Это входящее соединение? Peer_Comand_Interpretator(ClientID, *Buffer, Size, *Local_TorrentList) ; Обработка только входящих соединений сервера для уже установленых коннектов. Else *Local_TorrentList\TorrentList()\Network\PeerList()\InCon=#True ; Если #True, то пир установил входящее соединение. CloseNetworkConnection(ClientID) ; ИД в списке есть, тока не для входящих - закрыть коннект! EndIf x=#True ; В списке нашли такой ИД соединения. Break EndIf Next Next If x=#False ; Не найден такой ID соединения в списке, похоже что это новое соединение. If Peer_TestInConnect(*Local_TorrentList, ClientID)=#True ; Проверка можно ли принять входящее соединение от подключившегося пира Peer_Server_NewConnect_Interpretator(ClientID, *Buffer, Size, *Local_TorrentList.Core_Torrent) ; Обработка входящих соединений сервера для новых коннектов. Else ; Превышено число соединений. CloseNetworkConnection(ClientID) ; Пира в топку! EndIf EndIf EndProcedure Procedure.a Peer_ClientEvent(*Local_TorrentList.Core_Torrent, *Buffer) ; События клиента. Protected Result.a, Connect, CountBytes, *Point Protected Info.Peer_Handshake, Size.long Result = #False Connect = *Local_TorrentList\TorrentList()\Network\PeerList()\ConnectID If Connect If NetworkClientEvent(Connect)=#PB_NetworkEvent_Data ; Пир прислал данные. FillMemory(*Buffer, 65536, 0) ; Очистили память. CountBytes = ReceiveNetworkData(Connect, *Buffer, 65536) If CountBytes>0 If *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_ConnectStatus = #True Result = Peer_Comand_Interpretator(Connect, *Buffer, CountBytes, *Local_TorrentList) ; Обработка принятых данных. Else ; Пир еще не прошол процедуру "Рукопожатия", поэтому текущее сообщение должно быть ответом на наше "Рукопожатие". If CountBytes>=68 If Peer_In_Handshake(*Buffer, CountBytes, @Info) = #True ; "Рукопожатие" не содержит ошибок. If CompareMemory(@*Local_TorrentList\TorrentList()\INFO_HashBin[0], @Info\INFO_Hash[0], 20) = 1 ; Правильнаы хеш. *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_ConnectStatus = #True ; Успешный конект. *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_id = PeekS(@Info\Client, 20, #PB_Ascii) *Point = Peer_Create_BitField(*Local_TorrentList, @Size) ; Создание отчета о имеющихся частях для пира. If *Point If Size\l>5 Peer_AddList_OutData(*Local_TorrentList, *Point, Size\l) Else FreeMemory(*Point) EndIf EndIf If CountBytes>68 Peer_Comand_Interpretator(Connect, *Buffer+68, CountBytes-68, *Local_TorrentList) EndIf Else Peer_ClearPeer(*Local_TorrentList) ; Пира в топку. *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_ConnectStatus = #False EndIf Else Peer_ClearPeer(*Local_TorrentList) ; Пира в топку. *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_ConnectStatus = #False EndIf Else Peer_ClearPeer(*Local_TorrentList) ; Пира в топку. *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_ConnectStatus = #False EndIf EndIf EndIf EndIf EndIf ProcedureReturn Result EndProcedure ; Мьютекс G_TorrentList\Mutex НЕ ЗАХВАЧЕН!!!! Procedure Peer_GetConnectPeer(*Local_TorrentList.Core_Torrent) ; Чтение данных об установленых конектах с пирами. Protected INFO_Hash.s, IP_Peer.s, Port.u, x Protected *Point, i LockMutex(G_ProgramMiscInfo\Network\ConnectPeer_Mutex) ForEach G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List() INFO_Hash = G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List()\INFO_Hash x=#False ForEach *Local_TorrentList\TorrentList() If *Local_TorrentList\TorrentList()\INFO_Hash = INFO_Hash x=#True Break EndIf Next If x=#True ; Найден такой торрент в локальном списке. IP_Peer = G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List()\IP_Peer Port = G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List()\Port ForEach *Local_TorrentList\TorrentList()\Network\PeerList() ; Ищем пира, которому пренадлежит полученный идентификатор связи. If *Local_TorrentList\TorrentList()\Network\PeerList()\IP$=IP_Peer And *Local_TorrentList\TorrentList()\Network\PeerList()\Port=Port If *Local_TorrentList\TorrentList()\Network\PeerList()\ConnectID=0 If G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List()\ConnectID ; Успешое соединение с пиром *Local_TorrentList\TorrentList()\Network\PeerList()\ConnectID = G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List()\ConnectID G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List()\ConnectID=0 *Local_TorrentList\TorrentList()\Network\PeerList()\Err_ConnectCount=0 *Local_TorrentList\TorrentList()\Network\PeerList()\Connect_Status = #Peer_OutConnect ; Исходящее соединение, т. е. клиент установл связь с пиром. *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_Status\Choke=#True *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_Status\Interested=#False *Local_TorrentList\TorrentList()\Network\PeerList()\MyStatus\Choke=#True *Local_TorrentList\TorrentList()\Network\PeerList()\MyStatus\Interested=#False *Local_TorrentList\Info\Current_CountConnect+1 ; Общее число соединений. *Local_TorrentList\TorrentList()\Network\CountActivePeer+1 ; Число соединений текущего торрента. *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_ConnectStatus = #False ; Пока что пир еще не ответил на "рукопожатие". *Local_TorrentList\TorrentList()\Network\PeerList()\Err_ConnectCount = 0 ; Нет ошибок соедиения. x=ArraySize(*Local_TorrentList\TorrentList()\Torrent\MapFile\MapPiece()) If x<1 : x=0 : EndIf ReDim *Local_TorrentList\TorrentList()\Network\PeerList()\MapPiece(x) For i=0 To x *Local_TorrentList\TorrentList()\Network\PeerList()\MapPiece(i) = 0 ; Отмечаем что у пира нет чатей торрента. Далее have и bitfield собщения заполнят этот массив. Next i *Point = Peer_Create_Handshake(*Local_TorrentList) ; "Рукопожатие". If *Point ; Успешное генерирование "Рукопожатия". If Peer_AddList_OutData(*Local_TorrentList, *Point, 68) <> #True ; Добавить в список вывода данных. ; В случае, ошибки, разорвать связь с пиром. Peer_ClearPeer(*Local_TorrentList) EndIf Else ; В случае, ошибки, разорвать связь с пиром. Peer_ClearPeer(*Local_TorrentList) EndIf Else ; Неудалось соединенится с пиром. *Local_TorrentList\TorrentList()\Network\PeerList()\Err_ConnectCount+1 ; Прибавили 1 к числу ошибок содеинения. EndIf ; Время в котрое был установлено коннект. *Local_TorrentList\TorrentList()\Network\PeerList()\Time_Connect = *Local_TorrentList\Info\Current_Time *Local_TorrentList\TorrentList()\Network\PeerList()\MaxRequest = #Peer_MaxPeerRequest / 2 ; Максимально разрешенное число невыполненых запросов для этого пира. *Local_TorrentList\TorrentList()\Network\PeerList()\Time_UploadNoConnect = 0 Else ; Баг. Запрошенный пир уже подключен! If G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List()\ConnectID CloseNetworkConnection(G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List()\ConnectID) G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List()\ConnectID=0 EndIf EndIf Break EndIf Next EndIf If G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List()\ConnectID CloseNetworkConnection(G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List()\ConnectID) ; Какая-то ошибка и нужно разорвать соедитнение, EndIf DeleteElement(G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List()) Next UnlockMutex(G_ProgramMiscInfo\Network\ConnectPeer_Mutex) EndProcedure Procedure Peer_Connect_CountPeer() ; Узнаем сколько пиров всех торрентов ожидает конекта в списке заданий. Protected Result Result=0 LockMutex(G_ProgramMiscInfo\Network\ConnectPeer_Mutex) Result = ListSize(G_ProgramMiscInfo\Network\ConnectPeer_List()) ; Список еще не выполеных заданий. Result + ListSize(G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List()) ; Список уже приконектенных пиров, но еще не обработаных. UnlockMutex(G_ProgramMiscInfo\Network\ConnectPeer_Mutex) ProcedureReturn Result EndProcedure ; В списке должен быть выбран требуемый пир, требуемого торрента. Procedure Peer_AddConnectPeer(*Local_TorrentList.Core_Torrent, *Count.Long) ; Добавление пира в список заданих конекта. Protected INFO_Hash.s, IP_Peer.s, Port.u, x Protected SemaphoreState.a, CountPeer, AllCountPeer ;ProcedureReturn INFO_Hash = *Local_TorrentList\TorrentList()\INFO_Hash IP_Peer = *Local_TorrentList\TorrentList()\Network\PeerList()\IP$ Port = *Local_TorrentList\TorrentList()\Network\PeerList()\Port SemaphoreState = #False LockMutex(G_ProgramMiscInfo\Network\ConnectPeer_Mutex) ; Число пиров, с которыми еще не установлена связь, т. е. они в очереди на соединение. CountPeer=0 ForEach G_ProgramMiscInfo\Network\ConnectPeer_List() If G_ProgramMiscInfo\Network\ConnectPeer_List()\INFO_Hash = INFO_Hash CountPeer+1 EndIf Next ForEach G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List() If G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List()\INFO_Hash = INFO_Hash CountPeer+1 EndIf Next If *Count *Count\l = CountPeer EndIf AllCountPeer=CountPeer+*Local_TorrentList\TorrentList()\Network\CountActivePeer ; Не превышено ли число допустимых соединений? If AllCountPeer < G_ProgramSetting\Lan\MaxPeer And AllCountPeer < G_ProgramSetting\Lan\MaxConnect And CountPeer<20 And G_ProgramMiscInfo\Network\ConnectPeer_CountPeerConnect<=20; ; Есть ли такое задание в списе заданий. x=#False ForEach G_ProgramMiscInfo\Network\ConnectPeer_List() If G_ProgramMiscInfo\Network\ConnectPeer_List()\INFO_Hash=INFO_Hash And G_ProgramMiscInfo\Network\ConnectPeer_List()\IP_Peer=IP_Peer And G_ProgramMiscInfo\Network\ConnectPeer_List()\Port=Port x=#True ; В списке есть такое задание. Break EndIf Next If x=#False ; В списке заданий, данных о пире нет, посмотрим в списке выполненных заданий. x=#False ForEach G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List() If G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List()\INFO_Hash=INFO_Hash And G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List()\IP_Peer=IP_Peer And G_ProgramMiscInfo\Network\ConnectPeer_ResiltID_List()\Port=Port x=#True ; В списке выполненых заданий есть такое задание. Break EndIf Next If x=#False ; Добавляем новое задание. x=ListSize(G_ProgramMiscInfo\Network\ConnectPeer_List()) If x>0 If x=1 AllCountPeer=0 Else AllCountPeer=Random(x-1) EndIf SelectElement(G_ProgramMiscInfo\Network\ConnectPeer_List(), AllCountPeer) EndIf If x<1000 If AddElement(G_ProgramMiscInfo\Network\ConnectPeer_List()) G_ProgramMiscInfo\Network\ConnectPeer_List()\INFO_Hash = INFO_Hash G_ProgramMiscInfo\Network\ConnectPeer_List()\IP_Peer = IP_Peer G_ProgramMiscInfo\Network\ConnectPeer_List()\Port = Port G_ProgramMiscInfo\Network\ConnectPeer_List()\Active = #False SemaphoreState = #True EndIf EndIf EndIf EndIf EndIf UnlockMutex(G_ProgramMiscInfo\Network\ConnectPeer_Mutex) EndProcedure Procedure Peer_CloseBadPeer(*Local_TorrentList.Core_Torrent, MaxConnectState.a) ; Поиск пира, который меньше всего отдал данных и разрыв связи с ним. Protected x, BadPeer, MinInBytes.q, InBytes.q Protected Temp ; Ищем пира с которым пустой коннект т. е. нет обмена данными и он блокирует нас. x=#False ForEach *Local_TorrentList\TorrentList()\Network\PeerList() If *Local_TorrentList\TorrentList()\Network\PeerList()\ConnectID ; Пир вообще ни разу не разблокировал клиента и он в нас незаинтерисован. ; Превышено время If *Local_TorrentList\TorrentList()\Network\PeerList()\InDataTime > #Peer_BreakConnect_Time ; Допустимое время неактивности пира. If *Local_TorrentList\Info\Current_Time - *Local_TorrentList\TorrentList()\Network\PeerList()\Time_Connect > #Peer_BreakConnect_Time If *Local_TorrentList\TorrentList()\TorrentStatus = #TorrentStatus_Seed If *Local_TorrentList\TorrentList()\Network\PeerList()\Connect_Status = #Peer_OutConnect *Local_TorrentList\TorrentList()\Network\PeerList()\Time_UploadNoConnect = *Local_TorrentList\Info\Current_Time + 10*60 ; 10 минут не устанавливаем связь с этим пиром. EndIf EndIf ; Время превышено - пира в топку. Peer_ClearPeer(*Local_TorrentList) *Local_TorrentList\TorrentList()\Network\PeerList()\Err_ConnectCount = 4 ; Блокируем подключение пира в течение 8 минут. x=#True ;Break EndIf EndIf EndIf Next If x=#False And MaxConnectState=#True ; Превышено число соединений. x=#False BadPeer=-1 : x=0 MinInBytes = $7FFFFFFFFFFFFFFF : Temp=0 ForEach *Local_TorrentList\TorrentList()\Network\PeerList() If *Local_TorrentList\TorrentList()\Network\PeerList()\Active = #True ; Пир у нас не качает. If *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_Status\Interested<>#True ;*Local_TorrentList\TorrentList()\Network\PeerList()\PeerDown<>#True InBytes = *Local_TorrentList\TorrentList()\Network\PeerList()\Total_InBytes ; Сколько байт нам передал пир. If InBytes < MinInBytes If *Local_TorrentList\Info\Current_Time - *Local_TorrentList\TorrentList()\Network\PeerList()\Time_Connect > #Peer_BreakConnect_Time If *Local_TorrentList\TorrentList()\Network\PeerList()\Average_InSpeed<#Peer_BestPeer_MinInSpeed MinInBytes = InBytes BadPeer=x EndIf EndIf EndIf EndIf If *Local_TorrentList\TorrentList()\Network\PeerList()\Average_InSpeed>#Peer_BestPeer_MinInSpeed*2 Temp+1 EndIf EndIf x+1 Next If BadPeer>=0 And Temp>=4 ; Этот пир нам меньше всего отдал информации. SelectElement(*Local_TorrentList\TorrentList()\Network\PeerList(), BadPeer) Peer_ClearPeer(*Local_TorrentList) ; Деактивация текущего пира. EndIf EndIf EndProcedure Procedure.a Peer_ConnectTime(*Local_TorrentList.Core_Torrent) ; Формировние таймаутов связи с пиром, если с ним не удалось установить конект. Protected Result.a, Time.l, Count, Delay Protected PeerTime.l Time=*Local_TorrentList\Info\Current_Time Result=#False Count=*Local_TorrentList\TorrentList()\Network\PeerList()\Err_ConnectCount If Count>0 PeerTime = *Local_TorrentList\TorrentList()\Network\PeerList()\Time_Connect If Count<#Peer_MaxErrConnect Select Count Case 1 Delay=2*60 Case 2 Delay=4*60 Case 3, 4 Delay=8*60 Case 5, 6 Delay=10*60 Case 7 Delay=16*60 Default Delay=20*60 EndSelect If Time-PeerTime >=Delay Result=#True EndIf Else *Local_TorrentList\TorrentList()\Network\PeerList()\Active=-1 ; Удаление пира. EndIf Else Result=#True EndIf ProcedureReturn Result EndProcedure Procedure.a Peer_Core(*Local_TorrentList.Core_Torrent, *Buffer) ; Обработка данных всех пиров (входящие и исходящие соединения). Static CurrentElement, AllTorrent_Result.a, RequestCounter.l Protected Result.a, Result_1.a, Temp, Status Protected i, x Result=#False Temp=ListSize(*Local_TorrentList\TorrentList())-1 If Temp>=0 If CurrentElement>Temp CurrentElement=0 AllTorrent_Result=#False EndIf Repeat SelectElement(*Local_TorrentList\TorrentList(), CurrentElement) ; За один вызов процедуры обрабатывается один торрент. Status = *Local_TorrentList\TorrentList()\TorrentStatus ; Статус торрента. If Status=#TorrentStatus_Load Or Status=#TorrentStatus_Seed Or Status=#TorrentStatus_Pause Break EndIf CurrentElement+1 Until CurrentElement>Temp If CurrentElement<=Temp If Status=#TorrentStatus_Load And G_ProgramMiscInfo\Torrent_All_Pause<>#True; Загружаем торрент. If Peer_Download(*Local_TorrentList.Core_Torrent)=#True ; Генерация запросов пирам при скачивании торрентов. Result=#True EndIf EndIf If Status=#TorrentStatus_Load Or Status=#TorrentStatus_Seed Or Status=#TorrentStatus_Pause ForEach *Local_TorrentList\TorrentList()\Network\PeerList() ; Просматриваем список пиров текущего торрента. If *Local_TorrentList\TorrentList()\Network\PeerList()\ConnectID x=0 : i=0 While ListSize(*Local_TorrentList\TorrentList()\Network\PeerList()\QueueList())>0 x=Peer_SendData(*Local_TorrentList.Core_Torrent, @RequestCounter) ; Отослали данные пиру. If x>0 Else Break EndIf Temp+x i+1 If Temp>=1024 Or i>=10 : Break : EndIf ;32767 Передано больше заданого числа байт. Wend If *Local_TorrentList\TorrentList()\Network\PeerList()\Connect_Status = #Peer_OutConnect ; Исходящее соединение, т. е. клиент установл связь с пиром. If Peer_ClientEvent(*Local_TorrentList, *Buffer)=#True ; События клиента. Result=#True EndIf EndIf EndIf Next EndIf CurrentElement+1 If Result<>#True And RequestCounter>0 Result=#True RequestCounter-1 EndIf If Result=#True And AllTorrent_Result<>#True AllTorrent_Result=#True EndIf EndIf Else AllTorrent_Result=#False EndIf Result_1 = AllTorrent_Result ProcedureReturn Result_1 EndProcedure Procedure Peer_ServerDisconnect(*Local_TorrentList.Core_Torrent, ClientID) ; Вызываеться при событии #PB_NetworkEvent_Disconnect сервера. Protected i, x ForEach *Local_TorrentList\TorrentList() ForEach *Local_TorrentList\TorrentList()\Network\PeerList() If *Local_TorrentList\TorrentList()\Network\PeerList()\ConnectID = ClientID ; Нашли такой идентификатор открытого соединения в списке. Peer_DelPeel_GetPieces_List(*Local_TorrentList.Core_Torrent, ClientID) ; Удаление пира из списка запросов блоков, частей этого торрента. If *Local_TorrentList\TorrentList()\TorrentStatus = #TorrentStatus_Seed If *Local_TorrentList\TorrentList()\Network\PeerList()\Connect_Status = #Peer_OutConnect *Local_TorrentList\TorrentList()\Network\PeerList()\Time_UploadNoConnect = *Local_TorrentList\Info\Current_Time + 20*60 ; 20 минут не устанавливаем связь с этим пиром. EndIf EndIf *Local_TorrentList\TorrentList()\Network\PeerList()\ConnectID=0 Peer_ClearPeer(*Local_TorrentList) *Local_TorrentList\Info\Current_CountConnect - 1 ; Общее число соединений. *Local_TorrentList\TorrentList()\Network\CountActivePeer-1 ; Общее число пиров текущего торрента, с которыми установлен коннект. Break EndIf Next Next EndProcedure ; Захвачен мьюльтекс "G_TorrentList\Mutex". И должен быть выбран текущий торрент в списке. Procedure Peer_CopyInfo_Modify_Network_IO(*Local_TorrentList.Core_Torrent) ; Копирование из локального списка в основной, информации об обмене данными по сети. Protected Seeds, AllPeers, AllSeeds, ActivePeers Protected CountPiece, Temp Protected TempS.s, x, i, TempF.f Protected PieceSize, piece_length.l, TorrentSize.q;ArraySize Seeds = 0 AllPeers=0 AllSeeds=0 ActivePeers=0 CountPiece = *Local_TorrentList\TorrentList()\Torrent\CountPiece piece_length = *Local_TorrentList\TorrentList()\piece_length TorrentSize = *Local_TorrentList\TorrentList()\All_Size ; Размер торрента. G_TorrentList\TorrentList()\Network_IO\Excess + *Local_TorrentList\TorrentList()\Network_IO\Excess *Local_TorrentList\TorrentList()\Network_IO\Excess = 0 ; Сколько было скачано/отдано пирам конктретного трекера. ForEach *Local_TorrentList\TorrentList()\Network\Tracker() *Local_TorrentList\TorrentList()\Network\Tracker()\Downloaded=0 *Local_TorrentList\TorrentList()\Network\Tracker()\Uploaded = 0 x = *Local_TorrentList\TorrentList()\Network\Tracker()\NumTracker ForEach *Local_TorrentList\TorrentList()\Network\PeerList() If *Local_TorrentList\TorrentList()\Network\PeerList()\Active = #True If *Local_TorrentList\TorrentList()\Network\PeerList()\NumTracker = x *Local_TorrentList\TorrentList()\Network\Tracker()\Downloaded + *Local_TorrentList\TorrentList()\Network\PeerList()\InSpeed *Local_TorrentList\TorrentList()\Network\Tracker()\Uploaded + *Local_TorrentList\TorrentList()\Network\PeerList()\OutSpeed EndIf EndIf Next CompilerIf #Debug_Mode = #True ;*Local_TorrentList\TorrentList()\Network\Tracker()\Downloaded=0 CompilerEndIf Next ; Скорость обмена данными текущего торрента. ForEach *Local_TorrentList\TorrentList()\Network\PeerList() If *Local_TorrentList\TorrentList()\Network\PeerList()\Active = #True *Local_TorrentList\TorrentList()\Network_IO\InSpeed + *Local_TorrentList\TorrentList()\Network\PeerList()\InSpeed *Local_TorrentList\TorrentList()\Network_IO\OutSpeed + *Local_TorrentList\TorrentList()\Network\PeerList()\OutSpeed EndIf If CountPiece = Torrent_CountPiece(*Local_TorrentList\TorrentList()\Network\PeerList()\MapPiece(), CountPiece) AllSeeds + 1 If *Local_TorrentList\TorrentList()\Network\PeerList()\Active = #True Seeds+1 EndIf Else AllPeers + 1 If *Local_TorrentList\TorrentList()\Network\PeerList()\Active = #True ActivePeers+1 EndIf EndIf x = *Local_TorrentList\TorrentList()\Network\PeerList()\ip ForEach G_TorrentList\TorrentList()\Network\PeerList() If G_TorrentList\TorrentList()\Network\PeerList()\ip = x If *Local_TorrentList\TorrentList()\Network\PeerList()\Active = #True ; Флаги пира. TempS="" If *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_Status\Interested=#True ;*Local_TorrentList\TorrentList()\Network\PeerList()\PeerDown=#True ;*Local_TorrentList\TorrentList()\Network\PeerList()\OutSpeed>0 If *Local_TorrentList\TorrentList()\Network\PeerList()\MyStatus\Choke=#False TempS+"U" ; В данный момент отдается (заинтересован и доступен). ElseIf *Local_TorrentList\TorrentList()\Network\PeerList()\MyStatus\Choke=#True TempS+"u" ; Пир хочет у вас скачать, но вы еще не отдаете (заинтересован, но занят). EndIf EndIf If *Local_TorrentList\TorrentList()\Network\PeerList()\MyStatus\Interested=#True ;*Local_TorrentList\TorrentList()\Network\PeerList()\PeerUp=#True ;*Local_TorrentList\TorrentList()\Network\PeerList()\InSpeed>0 If *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_Status\Choke=#False TempS+"D" ; В данный момент скачивается (заинтересован и доступен). ElseIf *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_Status\Choke=#True TempS+"d" ; Ваш клиент хочет скачать, но пир не хочет отдавать (заинтересован, но занят). EndIf EndIf If *Local_TorrentList\TorrentList()\Network\PeerList()\InCon=#True TempS+"I" ; Входящее подключение EndIf If *Local_TorrentList\TorrentList()\Network\PeerList()\TimeOut>=#Peer_Time_Send_KeepAlive ; Пир долго не присылал сообщения. TempS+"S" ; Пир неактивен (спит). EndIf G_TorrentList\TorrentList()\Network\PeerList()\Out_Table\PeerFlags = TempS ; Запросы пиру и от него. If *Local_TorrentList\TorrentList()\TorrentStatus = #TorrentStatus_Load TempS="("+Str(*Local_TorrentList\TorrentList()\Network\PeerList()\CurrentRequest)+") " Else TempS="" EndIf G_TorrentList\TorrentList()\Network\PeerList()\Out_Table\Request=TempS+Str(*Local_TorrentList\TorrentList()\Network\PeerList()\CountOut_Request)+" | "+Str(*Local_TorrentList\TorrentList()\Network\PeerList()\CountIn_Request) ; Сколько процентов данных есть у пира. Temp = Torrent_CountPiece(*Local_TorrentList\TorrentList()\Network\PeerList()\MapPiece(), CountPiece) If Temp>0 TempF=CountPiece/Temp If TempF>0 G_TorrentList\TorrentList()\Network\PeerList()\Out_Table\Percent = 1000/TempF Else G_TorrentList\TorrentList()\Network\PeerList()\Out_Table\Percent = 0 EndIf Else G_TorrentList\TorrentList()\Network\PeerList()\Out_Table\Percent = 0 EndIf ; Скорость приема от пира. *Local_TorrentList\TorrentList()\Network\PeerList()\Average_InSpeed = Add_AverageList_Long(*Local_TorrentList\TorrentList()\Network\PeerList()\Average_InSpeed_List(), *Local_TorrentList\TorrentList()\Network\PeerList()\InSpeed, 10) G_TorrentList\TorrentList()\Network\PeerList()\Out_Table\InBytes = *Local_TorrentList\TorrentList()\Network\PeerList()\Average_InSpeed ; Скорость передачи пиру. *Local_TorrentList\TorrentList()\Network\PeerList()\Average_OutSpeed = Add_AverageList_Long(*Local_TorrentList\TorrentList()\Network\PeerList()\Average_OutSpeed_List(), *Local_TorrentList\TorrentList()\Network\PeerList()\OutSpeed, 10) G_TorrentList\TorrentList()\Network\PeerList()\Out_Table\OutBytes = *Local_TorrentList\TorrentList()\Network\PeerList()\Average_OutSpeed ; Принято от пира. *Local_TorrentList\TorrentList()\Network\PeerList()\Total_InBytes + *Local_TorrentList\TorrentList()\Network\PeerList()\InSpeed G_TorrentList\TorrentList()\Network\PeerList()\Out_Table\All_Download = *Local_TorrentList\TorrentList()\Network\PeerList()\Total_InBytes ; Отдано пиру. *Local_TorrentList\TorrentList()\Network\PeerList()\Total_OutBytes + *Local_TorrentList\TorrentList()\Network\PeerList()\OutSpeed G_TorrentList\TorrentList()\Network\PeerList()\Out_Table\All_Upload = *Local_TorrentList\TorrentList()\Network\PeerList()\Total_OutBytes ; Скорость пира. If *Local_TorrentList\TorrentList()\Network\PeerList()\CountHave>0 Temp = *Local_TorrentList\TorrentList()\Network\PeerList()\CountHave * piece_length ;#Peer_PieceLengthCountPiece Else Temp = 0 EndIf G_TorrentList\TorrentList()\Network\PeerList()\Out_Table\In_Speed_Peer = Add_AverageList_Long(*Local_TorrentList\TorrentList()\Network\PeerList()\Average_In_Speed_Peer_List(), Temp, 10) EndIf Break EndIf Next *Local_TorrentList\TorrentList()\Network\PeerList()\CountIn_Request=0 *Local_TorrentList\TorrentList()\Network\PeerList()\CountOut_Request=0 *Local_TorrentList\TorrentList()\Network\PeerList()\InSpeed = 0 *Local_TorrentList\TorrentList()\Network\PeerList()\OutSpeed = 0 *Local_TorrentList\TorrentList()\Network\PeerList()\CountHave = 0 Next ; Общая скорость обмена всех торрентов. *Local_TorrentList\Info\InSpeed + *Local_TorrentList\TorrentList()\Network_IO\InSpeed *Local_TorrentList\Info\OutSpeed + *Local_TorrentList\TorrentList()\Network_IO\OutSpeed ; Средняя скорость торрента. G_TorrentList\TorrentList()\Average_InSpeed = Add_AverageList_Long(G_TorrentList\TorrentList()\Average_InSpeed_List(), *Local_TorrentList\TorrentList()\Network_IO\InSpeed, 10) G_TorrentList\TorrentList()\Average_OutSpeed = Add_AverageList_Long(G_TorrentList\TorrentList()\Average_OutSpeed_List(), *Local_TorrentList\TorrentList()\Network_IO\OutSpeed, 10) G_TorrentList\TorrentList()\Network_IO\InSpeed = *Local_TorrentList\TorrentList()\Network_IO\InSpeed G_TorrentList\TorrentList()\Network_IO\OutSpeed = *Local_TorrentList\TorrentList()\Network_IO\OutSpeed G_TorrentList\TorrentList()\Network_IO\Seeds = Seeds G_TorrentList\TorrentList()\Network_IO\AllPeers = AllPeers G_TorrentList\TorrentList()\Network_IO\AllSeeds = AllSeeds G_TorrentList\TorrentList()\Network_IO\ActivePeers = ActivePeers G_TorrentList\TorrentList()\Network_IO\ReceiveBytes + G_TorrentList\TorrentList()\Network_IO\InSpeed G_TorrentList\TorrentList()\Network_IO\SendBytes + G_TorrentList\TorrentList()\Network_IO\OutSpeed ; Коэффициент раздачи. If G_TorrentList\TorrentList()\Network_IO\ReceiveBytes>0 G_TorrentList\TorrentList()\Network_IO\RatioDistribution = G_TorrentList\TorrentList()\Network_IO\SendBytes / G_TorrentList\TorrentList()\Network_IO\ReceiveBytes Else G_TorrentList\TorrentList()\Network_IO\RatioDistribution = G_TorrentList\TorrentList()\Network_IO\SendBytes / G_TorrentList\TorrentList()\TorrentFile\All_Size;0 EndIf Temp = G_TorrentList\TorrentList()\TorrentStatus If Temp = #TorrentStatus_Load Or Temp = #TorrentStatus_Pause ; Сколько осталось времени в секундах до конца загрузки. If G_TorrentList\TorrentList()\Average_InSpeed>0 G_TorrentList\TorrentList()\Network_IO\RemainsTime = (G_TorrentList\TorrentList()\Torrent\Torrent_FileSize-G_TorrentList\TorrentList()\Torrent\CurrentFileSize) / G_TorrentList\TorrentList()\Average_InSpeed Else G_TorrentList\TorrentList()\Network_IO\RemainsTime = -1 EndIf ; Информация об скачиваемых частях. Temp=ListSize(*Local_TorrentList\TorrentList()\GetPieces_List()) x=ListSize(G_TorrentList\TorrentList()\BlockList()) If Temp<>x If Temp>x ; В глобальной списке меньше элементов чем в локальном - нужно добавить. LastElement(G_TorrentList\TorrentList()\BlockList()) For i=x+1 To Temp AddElement(G_TorrentList\TorrentList()\BlockList()) Next i ElseIf Temp=0 If NextElement(G_TorrentList\TorrentList()\BlockList()) x=*Local_TorrentList\TorrentList()\GetPieces_List()\Piece G_TorrentList\TorrentList()\BlockList()\Piece = x ; Номер части. PieceSize=0 If x+1=CountPiece ; Это последняя часть PieceSize=TorrentSize-((x+1)*piece_length) Else ; Это не последняя часть. PieceSize=piece_length ; Размер части EndIf G_TorrentList\TorrentList()\BlockList()\Size = PieceSize ; Размер части G_TorrentList\TorrentList()\BlockList()\CountBlock = Temp+1 ; Количество блоков в части. If ArraySize(G_TorrentList\TorrentList()\BlockList()\BlockInfo())<>Temp ReDim G_TorrentList\TorrentList()\BlockList()\BlockInfo(Temp) EndIf For i=0 To Temp If *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\ConnectID ; Блок запрошен. If *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\Point G_TorrentList\TorrentList()\BlockList()\BlockInfo(i)=2 ; Блок получен. Else G_TorrentList\TorrentList()\BlockList()\BlockInfo(i)=1 ; Блок еще не получен. EndIf Else G_TorrentList\TorrentList()\BlockList()\BlockInfo(i)=0 EndIf Next i Temp=0 ForEach *Local_TorrentList\TorrentList()\Network\PeerList() If *Local_TorrentList\TorrentList()\Network\PeerList()\Active = #True If Torrent_GetArray_MapPiece(*Local_TorrentList\TorrentList()\Network\PeerList()\MapPiece(), x) Temp+1 EndIf EndIf Next G_TorrentList\TorrentList()\BlockList()\CountAvailable = Temp ; У скольких пиров есть эта часть. Else Break EndIf EndIf Next EndIf Else If ListSize(G_TorrentList\TorrentList()\BlockList())>0 ClearList(G_TorrentList\TorrentList()\BlockList()) EndIf EndIf *Local_TorrentList\TorrentList()\Network_IO\InSpeed=0 *Local_TorrentList\TorrentList()\Network_IO\OutSpeed=0 EndProcedure ; Захвачен мьюльтекс "G_TorrentList\Mutex". И должен быть выбран текущий торрент в списке. Procedure Peer_CopyInfo_Modify_AccessiblyPiece(*Local_TorrentList.Core_Torrent) ; Выявление доступных частей и их количества и копирование этих данных в основной список. Protected Accessibly.f, CountPiece.l Protected i, x, Temp, ArraySize Accessibly=0 ArraySize=ArraySize(G_TorrentList\TorrentList()\Torrent\MapFile\MapPiece())-1 ; Число элементов массива частей. CountPiece = G_TorrentList\TorrentList()\Torrent\CountPiece ; Те части, что уже есть. CopyArray(G_TorrentList\TorrentList()\Torrent\MapFile\MapPiece(), G_TorrentList\TorrentList()\Network\AccessiblyPiece()) Temp = G_TorrentList\TorrentList()\Torrent\CurrentCountPiece If Temp>0 Accessibly = Temp / CountPiece Else Accessibly = 0 EndIf ; А теперь то, что есть у пиров с котрыми прроизводится обмен. ForEach *Local_TorrentList\TorrentList()\Network\PeerList() If *Local_TorrentList\TorrentList()\Network\PeerList()\ConnectID ; С этим пиром есть связь. If *Local_TorrentList\TorrentList()\Network\PeerList()\Active = #True For i=0 To ArraySize G_TorrentList\TorrentList()\Network\AccessiblyPiece(i) | *Local_TorrentList\TorrentList()\Network\PeerList()\MapPiece(i) Next i Temp = Torrent_CountPiece(*Local_TorrentList\TorrentList()\Network\PeerList()\MapPiece(), CountPiece) If Temp>0 Accessibly + (CountPiece / Temp) EndIf EndIf EndIf Next G_TorrentList\TorrentList()\Network_IO\Available = Accessibly EndProcedure ; При вызове процедуры, в основном и локальном списках, должны быть установлены правильные текущие элементы. ; Захвачен мьюльтекс "G_TorrentList\Mutex". Procedure Peer_CopyInfo_Modify(*Local_TorrentList.Core_Torrent, *SatusBar_PortSatus.Ascii, *ChokeCounter.Ascii) ; Копирование из основного списка в локальный и обратно. Protected x.a, Port.u Protected Count, SizeArray, i Protected INFO_Hash.s If *Local_TorrentList\TorrentList()\TorrentName <> G_TorrentList\TorrentList()\TorrentName *Local_TorrentList\TorrentList()\TorrentName = G_TorrentList\TorrentList()\TorrentName EndIf INFO_Hash = G_TorrentList\TorrentList()\TorrentFile\INFO_Hash x=#False ForEach *Local_TorrentList\TorrentList() If *Local_TorrentList\TorrentList()\INFO_Hash = INFO_Hash x=#True Break EndIf Next If x=#True ; Нашли в локальной стрктуре требуемый элемент. If *Local_TorrentList\TorrentList()\TorrentStatus<>#TorrentStatus_Error ; Нет ошибки. If G_TorrentList\TorrentList()\TorrentStatus=#TorrentStatus_Load And *Local_TorrentList\TorrentList()\TorrentStatus=#TorrentStatus_Seed ; Только что был скачан торрент и торрент переходит в сидитирование. G_TorrentList\TorrentList()\TorrentStatus = #TorrentStatus_Seed Count = ListIndex(G_TorrentList\TorrentList()) SetIcon_TorrentListIcon(#MainWin_ListIcon_Torrent, Count, #MainWin_TorrentIcon_up) Tracker_Completed(INFO_Hash) ; Собщает трекеру что мы стали сидом. Peer_FinishedTorrent(*Local_TorrentList) ; Вызывается при завершении скачивания. ElseIf (G_TorrentList\TorrentList()\TorrentStatus=#TorrentStatus_Load Or G_TorrentList\TorrentList()\TorrentStatus=#TorrentStatus_Seed) And *Local_TorrentList\TorrentList()\TorrentStatus=#TorrentStatus_Stop ; Был запущен торрент. Peer_StartTorrent(*Local_TorrentList) ; Запуск скачивания или раздачи торента. ElseIf G_TorrentList\TorrentList()\TorrentStatus=#TorrentStatus_Stop And (*Local_TorrentList\TorrentList()\TorrentStatus=#TorrentStatus_Load Or *Local_TorrentList\TorrentList()\TorrentStatus=#TorrentStatus_Seed Or *Local_TorrentList\TorrentList()\TorrentStatus=#TorrentStatus_Pause) ; Был остановлен торрент. Peer_StopTorrent(*Local_TorrentList) ; Остановка скачивания или раздачи торрента. EndIf *Local_TorrentList\TorrentList()\TorrentStatus = G_TorrentList\TorrentList()\TorrentStatus Else ; В локальной структуре хранится признак ошибки. If G_TorrentList\TorrentList()\TorrentStatus=#TorrentStatus_Load Or G_TorrentList\TorrentList()\TorrentStatus=#TorrentStatus_Seed If G_TorrentList\TorrentList()\TorrentStatus<>#TorrentStatus_Error G_TorrentList\TorrentList()\TorrentStatus = *Local_TorrentList\TorrentList()\TorrentStatus ; Если торрент активный, то помечаем как содержащий ошибку. Count = ListIndex(G_TorrentList\TorrentList()) SetIcon_TorrentListIcon(#MainWin_ListIcon_Torrent, Count, #MainWin_TorrentIcon_bag) ; Помечаем торрент значком ошибки. Peer_StopTorrent(*Local_TorrentList) ; Остановили торрент. EndIf Else *Local_TorrentList\TorrentList()\TorrentStatus = G_TorrentList\TorrentList()\TorrentStatus ; Если торрент не активный, то скидываем признак ошибки в локальной структуре. EndIf EndIf If G_TorrentList\TorrentList()\TorrentStatus=#TorrentStatus_Load Or G_TorrentList\TorrentList()\TorrentStatus=#TorrentStatus_Seed Or G_TorrentList\TorrentList()\TorrentStatus=#TorrentStatus_Pause If *SatusBar_PortSatus If *SatusBar_PortSatus\a = #PortIconState_No ; Нет иконки. *SatusBar_PortSatus\a = #PortIconState_No_InConnect ; Иконка "Есть входящие соединения". EndIf EndIf Peer_CopyInfo_Modify_Network_IO(*Local_TorrentList) ; -------- Части. If G_TorrentList\TorrentList()\Torrent\CurrentCountPiece <> *Local_TorrentList\TorrentList()\Torrent\CurrentCountPiece Or G_TorrentList\TorrentList()\Torrent\CurrentFileSize <> *Local_TorrentList\TorrentList()\Torrent\CurrentFileSize G_TorrentList\TorrentList()\Torrent\CurrentCountPiece = *Local_TorrentList\TorrentList()\Torrent\CurrentCountPiece G_TorrentList\TorrentList()\Torrent\CurrentFileSize = *Local_TorrentList\TorrentList()\Torrent\CurrentFileSize CopyArray(*Local_TorrentList\TorrentList()\Torrent\MapFile\MapPiece(), G_TorrentList\TorrentList()\Torrent\MapFile\MapPiece()) EndIf SizeArray = ArraySize(G_TorrentList\TorrentList()\Torrent\MapFile\MapFiles())-1 For i=0 To SizeArray G_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\RealFileSize = *Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\RealFileSize G_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\TestFile = *Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\TestFile *Local_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\Priorityt = G_TorrentList\TorrentList()\Torrent\MapFile\MapFiles(i)\Priorityt Next i ; -------- Трекеры. ForEach G_TorrentList\TorrentList()\Network\Tracker() x=#False i=G_TorrentList\TorrentList()\Network\Tracker()\NumTracker If i>=0 G_TorrentList\TorrentList()\Network\Tracker()\Left = G_TorrentList\TorrentList()\TorrentFile\All_Size - G_TorrentList\TorrentList()\Torrent\CurrentFileSize ForEach *Local_TorrentList\TorrentList()\Network\Tracker() If *Local_TorrentList\TorrentList()\Network\Tracker()\NumTracker = i G_TorrentList\TorrentList()\Network\Tracker()\Downloaded + *Local_TorrentList\TorrentList()\Network\Tracker()\Downloaded G_TorrentList\TorrentList()\Network\Tracker()\Uploaded + *Local_TorrentList\TorrentList()\Network\Tracker()\Uploaded x=#True Break EndIf Next If x=#False ; Похоже что в глобальном списке появился новый трекер. If AddElement(*Local_TorrentList\TorrentList()\Network\Tracker()) *Local_TorrentList\TorrentList()\Network\Tracker()\NumTracker=i x=#True EndIf EndIf Else ; Этот пир, толи сам подключился, а может был добавлен другим способом, не через трекер. x=#True EndIf If x=#True *Local_TorrentList\TorrentList()\Network\Tracker()\Downloaded = 0 *Local_TorrentList\TorrentList()\Network\Tracker()\Uploaded = 0 EndIf Next ; Из глобально списка был удален трекер. If ListSize(G_TorrentList\TorrentList()\Network\Tracker()) < ListSize(*Local_TorrentList\TorrentList()\Network\Tracker()) ForEach *Local_TorrentList\TorrentList()\Network\Tracker() x=#False i=*Local_TorrentList\TorrentList()\Network\Tracker()\NumTracker ForEach G_TorrentList\TorrentList()\Network\Tracker() If G_TorrentList\TorrentList()\Network\Tracker()\NumTracker=i x=#True Break EndIf Next If x=#False DeleteElement(*Local_TorrentList\TorrentList()\Network\Tracker()) EndIf Next EndIf ; -------- Пиры. ForEach G_TorrentList\TorrentList()\Network\PeerList() ; Копировние пиров из основного списа в локальный. If G_TorrentList\TorrentList()\Network\PeerList()\Active<>-1 ; Пир не помечен на удаление. G_TorrentList\TorrentList()\Network\PeerList()\Active = #False i=G_TorrentList\TorrentList()\Network\PeerList()\ip Port=G_TorrentList\TorrentList()\Network\PeerList()\Port x=#False ForEach *Local_TorrentList\TorrentList()\Network\PeerList() If *Local_TorrentList\TorrentList()\Network\PeerList()\ip = i And *Local_TorrentList\TorrentList()\Network\PeerList()\Port = Port x=#True Break EndIf Next If x=#True ; В локальном списке есть такой пир. If G_TorrentList\TorrentList()\Network\PeerList()\Peer_id<>*Local_TorrentList\TorrentList()\Network\PeerList()\Peer_id G_TorrentList\TorrentList()\Network\PeerList()\Peer_id = *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_id EndIf G_TorrentList\TorrentList()\Network\PeerList()\Active = *Local_TorrentList\TorrentList()\Network\PeerList()\Active ; Имя торрент клиента пира. If G_TorrentList\TorrentList()\Network\PeerList()\Active=#True And G_TorrentList\TorrentList()\Network\PeerList()\Out_Table\Client="" And G_TorrentList\TorrentList()\Network\PeerList()\Peer_id<>"" G_TorrentList\TorrentList()\Network\PeerList()\Out_Table\Client = ClientName(G_TorrentList\TorrentList()\Network\PeerList()\Peer_id) EndIf Else ; В локальном списке не найдено упоминание об этом пире - добавим его. If AddElement(*Local_TorrentList\TorrentList()\Network\PeerList()) Peer_CopyInfo_AddPeer(*Local_TorrentList) ; Добавление пира в локальный список. EndIf EndIf Else DeleteElement(G_TorrentList\TorrentList()\Network\PeerList()) EndIf Next ForEach *Local_TorrentList\TorrentList()\Network\PeerList() ; Копирование пиров из локального списка в основой. If *Local_TorrentList\TorrentList()\Network\PeerList()\Active <> -1 ; Пир не помечен на удаление. i=*Local_TorrentList\TorrentList()\Network\PeerList()\ip Port=*Local_TorrentList\TorrentList()\Network\PeerList()\Port x=#False ForEach G_TorrentList\TorrentList()\Network\PeerList() If G_TorrentList\TorrentList()\Network\PeerList()\ip = i And G_TorrentList\TorrentList()\Network\PeerList()\Port = Port x=#True Break EndIf Next If x=#False ; Нет такого элемента - надо создать. If AddElement(G_TorrentList\TorrentList()\Network\PeerList()) G_TorrentList\TorrentList()\Network\PeerList()\Peer_id = *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_id G_TorrentList\TorrentList()\Network\PeerList()\IP$ = *Local_TorrentList\TorrentList()\Network\PeerList()\IP$ G_TorrentList\TorrentList()\Network\PeerList()\ip = *Local_TorrentList\TorrentList()\Network\PeerList()\ip G_TorrentList\TorrentList()\Network\PeerList()\Port = *Local_TorrentList\TorrentList()\Network\PeerList()\Port G_TorrentList\TorrentList()\Network\PeerList()\NumTracker = *Local_TorrentList\TorrentList()\Network\PeerList()\NumTracker G_TorrentList\TorrentList()\Network\PeerList()\Active = *Local_TorrentList\TorrentList()\Network\PeerList()\Active G_TorrentList\TorrentList()\Network\PeerList()\Out_Table\Client = ClientName(*Local_TorrentList\TorrentList()\Network\PeerList()\Peer_id) EndIf Else If G_TorrentList\TorrentList()\Network\PeerList()\Peer_id<>*Local_TorrentList\TorrentList()\Network\PeerList()\Peer_id G_TorrentList\TorrentList()\Network\PeerList()\Peer_id = *Local_TorrentList\TorrentList()\Network\PeerList()\Peer_id EndIf G_TorrentList\TorrentList()\Network\PeerList()\Active = *Local_TorrentList\TorrentList()\Network\PeerList()\Active EndIf If *SatusBar_PortSatus If *SatusBar_PortSatus\a <> #PortIconState_Yes_InConnect If *Local_TorrentList\TorrentList()\Network\PeerList()\ConnectID And *Local_TorrentList\TorrentList()\Network\PeerList()\InCon=#True *SatusBar_PortSatus\a = #PortIconState_Yes_InConnect ; Иконка "Есть входящие соединения". EndIf EndIf EndIf Else ; Пир был помечен на удаление. Peer_ClearPeer(*Local_TorrentList) DeleteElement(*Local_TorrentList\TorrentList()\Network\PeerList()) EndIf Next If ListSize(G_TorrentList\TorrentList()\Network\PeerList()) <> ListSize(*Local_TorrentList\TorrentList()\Network\PeerList()) If ListSize(G_TorrentList\TorrentList()\Network\PeerList()) > ListSize(*Local_TorrentList\TorrentList()\Network\PeerList()) ; В основном списке больше пиров. ForEach G_TorrentList\TorrentList()\Network\PeerList() x=#False i=G_TorrentList\TorrentList()\Network\PeerList()\ip ForEach *Local_TorrentList\TorrentList()\Network\PeerList() If *Local_TorrentList\TorrentList()\Network\PeerList()\ip=i x=#True Break EndIf Next If x=#False DeleteElement(G_TorrentList\TorrentList()\Network\PeerList()) EndIf Next Else ; В дополнительном споске больше пиров. ForEach *Local_TorrentList\TorrentList()\Network\PeerList() x=#False i=*Local_TorrentList\TorrentList()\Network\PeerList()\ip ForEach G_TorrentList\TorrentList()\Network\PeerList() If G_TorrentList\TorrentList()\Network\PeerList()\ip=i x=#True Break EndIf Next If x=#False Peer_ClearQueueList(*Local_TorrentList) DeleteElement(*Local_TorrentList\TorrentList()\Network\PeerList()) EndIf Next EndIf EndIf Peer_CopyInfo_Modify_AccessiblyPiece(*Local_TorrentList) ; Выявление доступных частей и их количества и копирование этих данных в основной список. Peer_Download_Calc_MaxRequest(*Local_TorrentList) ; Расчет максимально допустимого числа невыполненых запрсов для конкретного пира. Peer_CloseBadPeer(*Local_TorrentList, 0) ; Поиск пира, который меньше всего отдал данных и разрыв связи с ним. Peer_Download_FindSleepPiece(*Local_TorrentList) ; Поиск частей, блоки котторых пиры не присылали в течение пределенного времени. Peer_Upload_RandomConnect(*Local_TorrentList) ; Выбор проихвольного пира для конекта с ним чтобы раздать ему торрент. If *ChokeCounter\a=0 Peer_Upload_Refresh(*Local_TorrentList) ; Вызывается раз в 10 секунд и определяет какие пиры будут качать от нашего клиента. EndIf EndIf EndIf EndProcedure ; При вызове процедуры, в основном списке, должн быть установлен правильный текущий элемент. ; Захвачен мьюльтекс "G_TorrentList\Mutex". Procedure Peer_CopyInfo_Add(*Local_TorrentList.Core_Torrent) ; Добавление торрента из основного списка в локальный. Protected x, i If AddElement(*Local_TorrentList\TorrentList()) *Local_TorrentList\TorrentList()\INFO_Hash = G_TorrentList\TorrentList()\TorrentFile\INFO_Hash *Local_TorrentList\TorrentList()\piece_length = G_TorrentList\TorrentList()\TorrentFile\piece_length *Local_TorrentList\TorrentList()\All_Size = G_TorrentList\TorrentList()\TorrentFile\All_Size CopyMemory(@G_TorrentList\TorrentList()\TorrentFile\INFO_HashBin[0], @*Local_TorrentList\TorrentList()\INFO_HashBin[0], 20) *Local_TorrentList\TorrentList()\TorrentStatus = G_TorrentList\TorrentList()\TorrentStatus FillMemory(@*Local_TorrentList\TorrentList()\Network_IO, SizeOf(Core_TorrentList_Network_IO), 0) *Local_TorrentList\TorrentList()\Network_IO\Excess = 0 CopyStructure(G_TorrentList\TorrentList()\Torrent, *Local_TorrentList\TorrentList()\Torrent, Sub_TorrentInfo_TorrentList_DataFile) x=ArraySize(G_TorrentList\TorrentList()\TorrentFile\pieces()) If x>=0 ReDim *Local_TorrentList\TorrentList()\SHA1_pieces(x) For i=0 To x *Local_TorrentList\TorrentList()\SHA1_pieces(i) = G_TorrentList\TorrentList()\TorrentFile\pieces(i)\SHA1_string Next i EndIf ForEach G_TorrentList\TorrentList()\Network\Tracker() If AddElement(*Local_TorrentList\TorrentList()\Network\Tracker()) *Local_TorrentList\TorrentList()\Network\Tracker()\NumTracker = G_TorrentList\TorrentList()\Network\Tracker()\NumTracker *Local_TorrentList\TorrentList()\Network\Tracker()\Downloaded = 0 *Local_TorrentList\TorrentList()\Network\Tracker()\Uploaded = 0 EndIf Next ForEach G_TorrentList\TorrentList()\Network\PeerList() If AddElement(*Local_TorrentList\TorrentList()\Network\PeerList()) Peer_CopyInfo_AddPeer(*Local_TorrentList) EndIf Next ; Размер кеша недавно запрошеных частей торрента и максимальное число, одновременно скачиваемых частей.. If *Local_TorrentList\TorrentList()\piece_length<=64*1024 ; 64 КБ. *Local_TorrentList\TorrentList()\File_Max_CountList = 80 *Local_TorrentList\TorrentList()\GetPieces_Count = 150 ;40 ElseIf *Local_TorrentList\TorrentList()\piece_length<=128*1024 ; 128 КБ. *Local_TorrentList\TorrentList()\File_Max_CountList = 32 *Local_TorrentList\TorrentList()\GetPieces_Count = 80 ;20 ElseIf *Local_TorrentList\TorrentList()\piece_length<=256*1024 ; 256 КБ. *Local_TorrentList\TorrentList()\File_Max_CountList = 16 *Local_TorrentList\TorrentList()\GetPieces_Count = 40 ;1 ElseIf *Local_TorrentList\TorrentList()\piece_length<=512*1024 ; 512 КБ. *Local_TorrentList\TorrentList()\File_Max_CountList = 8 *Local_TorrentList\TorrentList()\GetPieces_Count = 20 ;1 ElseIf *Local_TorrentList\TorrentList()\piece_length<=1*1024*1024 ; 1 МБ. *Local_TorrentList\TorrentList()\File_Max_CountList = 4 *Local_TorrentList\TorrentList()\GetPieces_Count = 10 ;4 ElseIf *Local_TorrentList\TorrentList()\piece_length<=4*1024*1024 ; 4 МБ. *Local_TorrentList\TorrentList()\File_Max_CountList = 2 *Local_TorrentList\TorrentList()\GetPieces_Count = 4 ;2 Else ; Больше 4 МБ. *Local_TorrentList\TorrentList()\File_Max_CountList = 1 *Local_TorrentList\TorrentList()\GetPieces_Count = 2 ;1 EndIf EndIf EndProcedure ; Захвачен мьюльтекс "G_TorrentList\Mutex". Procedure Peer_CopyInfo_Del(INFO_Hash.s, *Local_TorrentList.Core_Torrent) ; Удаление торрента из локального списка. ForEach *Local_TorrentList\TorrentList() If *Local_TorrentList\TorrentList()\INFO_Hash = INFO_Hash ; Разрыв связи со всеми пирами торрента. ForEach *Local_TorrentList\TorrentList()\Network\PeerList() Peer_ClearPeer(*Local_TorrentList) Next Peer_StopTorrent(*Local_TorrentList, #True) ; Отсановка торрента и освобождаение всех ресурсов, связанных с ним. DeleteElement(*Local_TorrentList\TorrentList()) Break EndIf Next EndProcedure Procedure Peer_CopyInfo(*Local_TorrentList.Core_Torrent, PauseCount) ; Копирование данных из структуры торрантов в локальную структуру и обратно. Protected NewList TorrentDel_List.s() ; Список торрентов, которые нужно удалить из локального ТоррентЛиста. Protected x, INFO_Hash.s, ListPos, TempF.f Protected SatusBar_PortSatus.Ascii, Size.q, CurrentFileSize.q Static ChokeCounter.Ascii Peer_GetConnectPeer(*Local_TorrentList) ; Изымание из очереди заданий данных о пирах, с которыми был установлен коннект. ClearList(TorrentDel_List()) ; Для страховки, на всякий случай. SatusBar_PortSatus\a=#PortIconState_No ; Нет иконки. LockMutex(G_TorrentList\Mutex) *Local_TorrentList\Info\InSpeed=0 *Local_TorrentList\Info\OutSpeed=0 ForEach G_TorrentList\TorrentList() INFO_Hash = G_TorrentList\TorrentList()\TorrentFile\INFO_Hash x=#False ForEach *Local_TorrentList\TorrentList() If *Local_TorrentList\TorrentList()\INFO_Hash = INFO_Hash ; В локальном списке есть этот торрент. x=#True Break EndIf Next ListPos = ListIndex(G_TorrentList\TorrentList()) If x=#True ; В локальном списуке есть такой торрент. If G_TorrentList\TorrentList()\TorrentStatus<>#TorrentStatus_Add And G_TorrentList\TorrentList()\TorrentStatus<>#TorrentStatus_Error And G_TorrentList\TorrentList()\DelStatus <> #True Peer_CopyInfo_Modify(*Local_TorrentList, @SatusBar_PortSatus, @ChokeCounter) EndIf Else ; В локальном списке нет такого торрента. If G_TorrentList\TorrentList()\TorrentStatus<>#TorrentStatus_Add And G_TorrentList\TorrentList()\TorrentStatus<>#TorrentStatus_Error And G_TorrentList\TorrentList()\DelStatus <> #True Peer_CopyInfo_Add(*Local_TorrentList) EndIf EndIf SelectElement(G_TorrentList\TorrentList(), ListPos) Next ChokeCounter\a + 1 ; Для вызова процедуры выбора качающих от нас пиров. If ChokeCounter\a > 10 ChokeCounter\a = 0 EndIf G_TorrentList\Info\PeerProc_Percent = Add_AverageList_Uncode(*Local_TorrentList\PeerProcList(), PauseCount, 4) ; Текущая загрузка этого потока. G_TorrentList\Info\Speed\InSpeed = *Local_TorrentList\Info\InSpeed G_TorrentList\Info\Speed\OutSpeed = *Local_TorrentList\Info\OutSpeed ; Общая средняя скорость. G_TorrentList\Info\Speed\Average_InSpeed = Add_AverageList_Long(G_TorrentList\Info\Speed\Average_InSpeed_List(), G_TorrentList\Info\Speed\InSpeed, 10) G_TorrentList\Info\Speed\Average_OutSpeed = Add_AverageList_Long(G_TorrentList\Info\Speed\Average_OutSpeed_List(), G_TorrentList\Info\Speed\OutSpeed, 10) ;If ListSize(G_TorrentList\TorrentList()) < ListSize(*Local_TorrentList\TorrentList()) ; Из основного списка был удален торрент. ForEach *Local_TorrentList\TorrentList() INFO_Hash = *Local_TorrentList\TorrentList()\INFO_Hash If *Local_TorrentList\TorrentList()\TorrentStatus=#TorrentStatus_Error If AddElement(TorrentDel_List()) TorrentDel_List() = INFO_Hash EndIf Else x=#False ForEach G_TorrentList\TorrentList() If G_TorrentList\TorrentList()\TorrentFile\INFO_Hash = INFO_Hash If G_TorrentList\TorrentList()\DelStatus <> #True x=#True EndIf Break EndIf Next If x=#False ; Торрент не найден в основном списке. If AddElement(TorrentDel_List()) TorrentDel_List() = INFO_Hash EndIf EndIf EndIf Next ;EndIf UnlockMutex(G_TorrentList\Mutex) If G_TorrentList\Info\PortIconState<>#PortIconState_ErrCreateServer ; Иконка "Ошибка создания сервера". G_TorrentList\Info\PortIconState = SatusBar_PortSatus\a EndIf If ListSize(TorrentDel_List())>0 ForEach TorrentDel_List() INFO_Hash = TorrentDel_List() Peer_CopyInfo_Del(INFO_Hash, *Local_TorrentList) Next ClearList(TorrentDel_List()) EndIf ; Проценты в таблице торрентов. If TryLockMutex(G_TorrentList\Mutex_Table) ForEach *Local_TorrentList\TorrentList() x=*Local_TorrentList\TorrentList()\TorrentStatus If x=#TorrentStatus_Load Or x=#TorrentStatus_Seed INFO_Hash = *Local_TorrentList\TorrentList()\INFO_Hash Size = *Local_TorrentList\TorrentList()\Torrent\Torrent_FileSize CurrentFileSize=*Local_TorrentList\TorrentList()\Torrent\CurrentFileSize If CurrentFileSize>0 TempF = 1000 / (Size / CurrentFileSize) ; Else TempF = 0 EndIf ForEach G_TorrentList\Table() If G_TorrentList\Table()\INFO_Hash = INFO_Hash G_TorrentList\Table()\Percent = Int(TempF) Break EndIf Next EndIf Next UnlockMutex(G_TorrentList\Mutex_Table) EndIf Core_Network_ConnectPeer_Thread(0) EndProcedure Procedure Peer_CountTimesPeer(Sec, *Local_TorrentList.Core_Torrent) ; Подсчет времени пиров для формирования keepalive запросов и закрытия соединения с клиентами, не присылающими запросы. Protected SizeArray, i, Time.l, Temp Time=*Local_TorrentList\Info\Current_Time ForEach *Local_TorrentList\TorrentList() ; Формирование #Peer_Message_KeepAlive сообщений и закрытие соединений с пирами по таймауту. ForEach *Local_TorrentList\TorrentList()\Network\PeerList() If *Local_TorrentList\TorrentList()\Network\PeerList()\ConnectID ; С этим пиром установен коннетк. *Local_TorrentList\TorrentList()\Network\PeerList()\TimeOut + Sec *Local_TorrentList\TorrentList()\Network\PeerList()\InDataTime + Sec If *Local_TorrentList\TorrentList()\Network\PeerList()\Connect_Status = #Peer_OutConnect ; Это исходящее соединение. If *Local_TorrentList\TorrentList()\Network\PeerList()\TimeOut>=#Peer_Time_Send_KeepAlive Peer_SendMessage(*Local_TorrentList, #Peer_Message_KeepAlive) EndIf ElseIf *Local_TorrentList\TorrentList()\Network\PeerList()\Connect_Status = #Peer_InConnect ; Это входящее соединение. If *Local_TorrentList\TorrentList()\Network\PeerList()\TimeOut >= #Peer_Time_Send_KeepAlive_ClosePeer ; Пир не присылал запросы в течение длительного времени и с ним разрывается соединение по таймауту. Peer_ClearPeer(*Local_TorrentList) EndIf EndIf EndIf Next ; Проверка чтобы блоки были присланы в течении допустимого времени. ForEach *Local_TorrentList\TorrentList()\GetPieces_List() SizeArray = ArraySize(*Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo()) For i=0 To SizeArray ; Пиру был отправлен запрос, но данные еще не получены от него. If *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\ConnectID And *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\Point=0 ; Пир не прислал данные (блок) за допустимый интервал времени. If Time - *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\CurrentTime > #Peer_Piece_TimeOut 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) *Local_TorrentList\TorrentList()\Network\PeerList()\CurrentRequest - 1 ; Текущее число запросов блоков для пира. Break EndIf Next EndIf *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\ConnectID = 0 *Local_TorrentList\TorrentList()\GetPieces_List()\PiecesInfo(i)\CurrentTime = 0 If *Local_TorrentList\TorrentList()\Current_CountBlockDownload>0 *Local_TorrentList\TorrentList()\Current_CountBlockDownload - 1 EndIf EndIf EndIf Next i Next Next EndProcedure DisableExplicit DataSection ; "BitTorrent protocol". Peer_Handshake_m: Data.a $42, $69, $74, $54, $6F, $72, $72, $65, $6E, $74 Data.a $20, $70, $72, $6F, $74, $6F, $63, $6F, $6C EndDataSection ; IDE Options = PureBasic 5.11 (Windows - x86) ; Folding = --------- ; EnableXP