unit PKSTController;

interface
uses
  IdThread, IdTCPClient,
  IdSocketHandle, ExtCtrls, IdStack, HashUnit, PKSTutils, Contnrs,
  PKSTconst, IdBaseComponent, IdComponent, IdUDPBase, IdUDPServer,
  windows, classes, sysutils;

Type
  TPKSTSubTitleEvent = procedure(Msg: string) of object;

  TPKSTUDPController = class;
  TPKSTTCPController = class;
  TPKSTHashManager = class;
  TPKSTTaskManager = class;
  TPKSTNodeManager = class;
  TPKSTDownLoadManager = class;
  TPKSTUpLoadManager = class;
  TPKSTTCPThread = Class;

  TPKSTController = class
  private
    Tick             : Currency;
    UpTime           : Currency;
    FNodeID          : TGUID;
    FNodeName        : string;
    FServerMode      : Boolean;
    FUseCenter       : Boolean;

    FOnConnect       : TNotifyEvent;         //TPKSTConnNotifyEvent;
    FOnDisConnect    : TNotifyEvent;         //TPKSTConnNotifyEvent;
    FOnDownloadNotify : TNotifyEvent;
    FOnScheduleChange : TNotifyEvent;
    FonUpLoadNotify  : TNotifyEvent;
    FOnSubTitle      : TPKSTSubTitleEvent;

    procedure SetDownloadNotify(Value: TNotifyEvent);

    procedure DoUDPRead(Sender: TObject; AData: TStream; ABinding: TIdSocketHandle);
    procedure DoConnect(AData: TStream; ABinding: TIdSocketHandle);
    procedure DoReply(AData: TStream; ABinding: TIdSocketHandle);
    procedure DoNodeInfo(AData: TStream; ABinding: TIdSocketHandle);
    procedure DoNodeList(AData: TStream; ABinding: TIdSocketHandle);
    procedure DoAskNList(AData: TStream; ABinding: TIdSocketHandle);
    procedure DoPublish(AData: TStream; ABinding: TIdSocketHandle);
    procedure DoDownload(AData: TStream; ABinding: TIdSocketHandle);
    procedure DoMSg(AData: TStream; ABinding: TIdSocketHandle);
    procedure DoData(AData: TStream; ABinding: TIdSocketHandle);
    procedure DoSubTitle(AData: TStream; ABinding: TIdSocketHandle);

    procedure SendConnect(AHost: string; Port: Integer);
    procedure REPLY(ABinding: TIdSocketHandle; var Data; Size: Integer); overload;
    procedure REPLY(Host: string; Port: Integer; var Data; Size: Integer); overload;

    procedure GetNodeInfo(AHost: string; Port: Integer);       //获取节点信息
    procedure SendNodeList(AHost: string; Port: Integer);      //发送节点列表
    procedure ASKNodeList(AHost: string; Port: Integer; ServerHost: string);      //请求节点列表
    procedure Download(AHost: string; Port: Integer; DownloadID: TGUID; FileHash: TMD5Digest; FileSize: Int64; FileName: string; start: int64); overload;                 //下载文件
    procedure Download(Node: TPKSTNode; DownloadID: TGUID; FileHash: TMD5Digest; FileSize: Int64; FileName: string; start: int64); overload;                 //下载文件
    procedure SendData(AHost: string; Port: Integer; DownloadID: TGUID; FileHash: TMD5Digest; FileSize: Int64; FileName: string; start: int64; RealFileName: string = '');    //传送数据

    procedure CheckInServerNodes(ServerHost: string; Node: TPKSTNode);
  protected
    _UDPController   : TPKSTUDPController;
    _NodeManager     : TPKSTNodeManager;
    _TaskManager     : TPKSTTaskManager;
    _DownloadManager : TPKSTDownLoadManager;
    _UpLoadManager   : TPKSTUpLoadManager;
    _HashManager     : TPKSTHashManager;

    _MaxDownloads    : Integer;
    _MaxUpLoads      : Integer;
    _RcvDir          : string;
    _CRCCheck        : Boolean;

    function GetNodes(I: Integer): TPKSTNodeInfo;
    function GetServerCount: Integer;
    function GetServerAdd(I: Integer): string;
    procedure SetMaxDownloads(Value: Integer);
    procedure SetMaxUpLoads(Value: Integer);

    function GetActiveNodeCount: Integer;
    function GetDeActiveNodeCount: Integer;
    function GetActiveNode(i: Integer): TPKSTNodeInfo;
    function GetDeActiveNode(i: Integer): TPKSTNodeInfo;

    function GetActiveServerCount: Integer;
    function GetDeActiveServerCount: Integer;
    function GetActiveServer(i: Integer): TPKSTNodeInfoEX;
    function GetDeActiveServer(i: Integer): TPKSTNodeInfoEX;
  public
    fileproc: on_fileproc;
    msgproc: on_msgproc;
    rcvproc: on_rcvproc;
    sendproc: on_sendproc;

    constructor Create(ServerMode: Boolean = False);
    destructor Destroy; override;

    Procedure Init(cfgfile : string); overload;
    Procedure Init(ID: TGUID; Serverlist: Tstringlist = nil); overload;
    Procedure Save(cfgfile : string);
    procedure InitServers(Serverlist: Tstrings);
    procedure Open;
    procedure Stop;

//    procedure DisConnect(AHost: string; Port: Integer);

    procedure Publish(FileNames: TStrings; Nodes: array of TPKSTNode; Pubenvent,Endenvent: TNotifyEvent);            //发布文件
    procedure PublishSubTitle(Msg: string; Nodes: array of TPKSTNode; Pubenvent,Endenvent: TNotifyEvent);            //发布文件

    function SendMsg(Node: TPKSTNode; Const Msg: string): integer;
    function SendMsgServer(Node: TPKSTNode; Const Msg: string): integer;

    property ServerMode: Boolean read FServerMode;
    property OnConnect: TNotifyEvent read FOnConnect write FOnConnect;
    property OnDisConnect: TNotifyEvent read FOnDisConnect write FOnDisConnect;
    property OnDownloadNotify: TNotifyEvent read FOnDownloadNotify write SetDownloadNotify;
    property OnScheduleChange: TNotifyEvent read FOnScheduleChange write FOnScheduleChange;
    property OnSubTitle: TPKSTSubTitleEvent read FOnSubTitle write FOnSubTitle;
    property OnUploadNotify: TNotifyEvent read FOnUploadNotify write FOnUploadNotify;

    property NodeID: TGUID read FNodeID;
    property NodeName: string read FNodeName write FNodeName;
    property MaxDownloads: Integer read _MaxDownloads write SetMaxDownloads;
    property MaxUpLoads: Integer read _MaxUpLoads write SetMaxUpLoads;
    property CRCCheck: Boolean read _CRCCheck write _CRCCheck;
    property RcvDir: string read _RcvDir write _RcvDir;
    property ServerCount: Integer read GetServerCount;
    property ServerAdd[I: Integer]: string read GetServerAdd;


    property ActiveNodeCount: Integer read GetActiveNodeCount;
    property ActiveNode[I: Integer]: TPKSTNodeInfo read GetActiveNode;
    property DeActiveNodeCount: Integer read GetDeActiveNodeCount;
    property DeActiveNode[I: Integer]: TPKSTNodeInfo read GetDeActiveNode;

    property ActiveServerCount: Integer read GetActiveServerCount;
    property ActiveServer[I: Integer]: TPKSTNodeInfoEX read GetActiveServer;
    property DeActiveServerCount: Integer read GetDeActiveServerCount;
    property DeActiveServer[I: Integer]: TPKSTNodeInfoEX read GetDeActiveServer;
  published

  end;

  TPKSTNodeManager = Class
  private
    _Timer: TTimer;
    _ServerMode   : Boolean;
    _ServerList   : TStringList;
    procedure ConnTime(Sender: TObject);
    function GetServerAdd(I: Integer): string;
  protected
    _Controller   : TPKSTController;
    ActiveNodes   : TObjectList;
    DeActiveNodes : TObjectList;
    procedure DeActive(Node: TObject);
    procedure ReConnectServer(Server: TPKSTNodeInfoEx);
    procedure KeepServerAlive(Server: TPKSTNodeInfoEx);

    function GetActiveNodeCount: Integer;
    function GetDeActiveNodeCount: Integer;
    function GetActiveNode(i: Integer): TPKSTNodeInfo;
    function GetDeActiveNode(i: Integer): TPKSTNodeInfo;

    function GetActiveServerCount: Integer;
    function GetDeActiveServerCount: Integer;
    function GetActiveServer(i: Integer): TPKSTNodeInfoEX;
    function GetDeActiveServer(i: Integer): TPKSTNodeInfoEX;

  public
    constructor Create(Controller: TPKSTController);
    destructor Destroy; override;

    procedure ProcNodeInfo(ANodeName: string; ANode: TPKSTNode; ANodeID: TGUID);
    procedure UpdateConnect(AHost: string; APort: Integer);
    function CheckActiveNode(AHost: string; APort: Integer): Boolean;

    function GetActiveNodeByHost(AHost: string; APort: Integer = PKST_CLIENTPORT): TPKSTNodeInfo;
    function GetDeActiveNodeByHost(AHost: string; APort: Integer = PKST_CLIENTPORT): TPKSTNodeInfo;
    function GetActiveServerByHost(AHost: string; APort: Integer = PKST_SERVERPORT): TPKSTNodeInfoEX;
    function GetDeActiveServerByHost(AHost: string; APort: Integer = PKST_SERVERPORT): TPKSTNodeInfoEX;

    procedure Open;
    procedure Stop;
    procedure InitServerList(ServerList: Tstrings);


    property ActiveNodeCount: Integer read GetActiveNodeCount;
    property ActiveNode[I: Integer]: TPKSTNodeInfo read GetActiveNode;
    property DeActiveNodeCount: Integer read GetDeActiveNodeCount;
    property DeActiveNode[I: Integer]: TPKSTNodeInfo read GetDeActiveNode;

    property ActiveServerCount: Integer read GetActiveServerCount;
    property ActiveServer[I: Integer]: TPKSTNodeInfoEX read GetActiveServer;
    property DeActiveServerCount: Integer read GetDeActiveServerCount;
    property DeActiveServer[I: Integer]: TPKSTNodeInfoEX read GetDeActiveServer;
  published

  end;

  TPKSTHashThread=class(TThread)
  private
    _CurHashData : TPKSTHashData;
  protected
    _Manager : TPKSTHashManager;
    procedure AddHashData;
    procedure SaveHashData;
    procedure Execute; override;
  public
    constructor Create(Manager : TPKSTHashManager); virtual;
    destructor Destroy; override;
  end;


  TPKSTHashManager = class
  private
    _HashThread: TPKSTHashThread;
    _HashPool: array of TPKSTHashData;
    _HashCount: Integer;
  protected
    _Controller: TPKSTController;
    FCacheFileName: string;
  public
    constructor Create(Controller: TPKSTController);
    destructor Destroy; override;
    procedure open;
    procedure stop;
    procedure CheckInHash(FileName: string; FileSize: Int64; FileTime: TFileTime; FileHash: TMD5Digest); overload;
    procedure CheckInHash(HashData: TPKSTHashData); overload;
    function GetHash(FileName: string; var Hash: TMD5Digest): Boolean;
    function CalcNewHash(FileName: string): TMD5Digest;
    procedure Save;

    property CacheFileName: string read FCacheFileName write FCacheFileName;
  published

  end;

  TPKSTUDPController = class
  private
    FonUDPRead: TUDPReadEvent;
  protected
    _Controller: TPKSTController;
    _server: TIdUDPServer;
  public
    constructor Create(Controller: TPKSTController);
    destructor Destroy; override;
    procedure open(Servermode: Boolean);
    procedure Stop;
    procedure Send(Host: string; Port: Integer; var Data; Size: Integer);

    property OnUDPRead: TUDPReadEvent read FOnUDPRead write FOnUDPRead;

  published

  end;

  TPKSTTCPController = class
  private
    _socket : TIdTCPClient;
    _thread : TPKSTTCPThread;
  protected
    _Controller: TPKSTController;

  public
    constructor Create(Controller: TPKSTController);
    destructor Destroy; override;
    procedure open(CenterHost: string; CenterPort: Integer = 80);
    procedure Stop;

  published

  end;

  TPKSTTCPThread = class(TIdThread)
  private
    _socket : TIdTCPClient;
  protected

    procedure Run; override;
  public
    constructor Create(socket : TIdTCPClient);
    destructor Destroy; override;
  published

  end;

  TPKSTBasicTask = class(TThread)
  private
    FonReply: TNotifyEvent;
  protected
    _Manager : TPKSTTaskManager;
    procedure DoSend; virtual;
    procedure Execute; override;
  public
    LastResponseTime: TDateTime;
    TimeOut: TDateTime;
    TimeOutCount: Integer;
    TaskID: TGUID;
    Host: string;
    Port: Integer;
    Node: TPKSTNode;
    SendData: array of Byte;
    SendSize: Integer;

    constructor Create(Manager : TPKSTTaskManager); virtual;
    destructor Destroy; override;
    procedure DoTimeOut; virtual; abstract;
    procedure ProcReply(var Data; Size: Integer); virtual;
    function Checktask(CheckID: TGUID): Boolean; virtual;
    property onReply: TNotifyEvent read FonReply write FonReply;
  published

  end;

  TPKSTNodeInfoTask = class(TPKSTBasicTask)
  private

  protected

  public
    constructor Create(Manager : TPKSTTaskManager); override;
    procedure DoTimeOut; override;
    procedure ProcReply(var Data; Size: Integer); override;
  end;


  TPKSTSendNListTask = class(TPKSTBasicTask)
  private

  protected

  public
    constructor Create(Manager : TPKSTTaskManager); override;
    procedure DoTimeOut; override;
    procedure ProcReply(var Data; Size: Integer); override;
  end;

  TPKSTAskNListTask = class(TPKSTBasicTask)
  private

  protected

  public
    constructor Create(Manager : TPKSTTaskManager); override;
    procedure DoTimeOut; override;
    procedure ProcReply(var Data; Size: Integer); override;
  end;

  TPKSTPublishDatas = record
    FileName: string;
    FileSize: Int64Rec;
    FileHash: TMD5Digest;
  end;

  TPKSTPublishTask = class(TPKSTBasicTask)
  private
    FonPublish: TNotifyEvent;
    FonEndPublish: TNotifyEvent;
  protected
    CurData: Integer;
    CurNode: Integer;

    procedure Execute; override;
    procedure prepare;
    procedure Publish;
    procedure PublishNext;
  public
    PubDatas : array of TPKSTPublishDatas;
    PubNodes : array of TPKSTNode;

    constructor Create(Manager : TPKSTTaskManager); override;
    function Checktask(CheckID: TGUID): Boolean; override;
    procedure DoTimeOut; override;
    procedure ProcReply(var Data; Size: Integer); override;

    property OnPublish: TNotifyEvent read FonPublish write FonPublish;
    property OnEndPublish: TNotifyEvent read FonEndPublish write FonEndPublish;
    property DataIndex: Integer read CurData;
    property NodeIndex: Integer read CurNode;
  end;

  TPKSTPublishSubTitleTask = class(TPKSTBasicTask)
  private
    FonPublish: TNotifyEvent;
    FonEndPublish: TNotifyEvent;
  protected
    CurNode: Integer;

    procedure Execute; override;
    procedure Publish;
    procedure PublishNext;
  public
    SubTitle : String;
    PubNodes : array of TPKSTNode;

    constructor Create(Manager : TPKSTTaskManager); override;
    procedure DoTimeOut; override;
    procedure ProcReply(var Data; Size: Integer); override;

    property OnPublish: TNotifyEvent read FonPublish write FonPublish;
    property OnEndPublish: TNotifyEvent read FonEndPublish write FonEndPublish;
    property NodeIndex: Integer read CurNode;
  end;

  TPKSTDownloadTask = class(TPKSTBasicTask)
  private

  protected

  public
    DownloadTaskID: TGUID;
    constructor Create(Manager : TPKSTTaskManager); override;
    procedure DoTimeOut; override;
    procedure ProcReply(var Data; Size: Integer); override;
  end;

  TPKSTMsgTask = class(TPKSTBasicTask)
  private

  protected
    procedure Execute; override;
  public
    constructor Create(Manager : TPKSTTaskManager); override;
    procedure DoTimeOut; override;
    procedure ProcReply(var Data; Size: Integer); override;
  end;


  TPKSTDataWindowRec = packed Record
    TaskID: TGUID;
    StartPos: int64;
    SendData: array of Byte;
    SendDataSize: Integer;
  end;

  TPKSTDataTask = class(TPKSTBasicTask)
  private
    _File: THandle;
    _UpLoadManaget: TPKSTUpLoadManager;

    _DataWindow: array [0..PKST_DATAWINDOW-1] of TPKSTDataWindowRec;
    _DataWindowPos: Integer;
    _WINDOWSize: Integer;
    _PackageLevel: Integer;
    _RetryFlag: Boolean;
//    PrvTaskID: TGUID;
//    PrvSendData: array of Byte;
//    PrvSendSize: Integer;
  protected
    _Trying: Boolean;
    _TryingCount: integer;
    ReadSize: cardinal;
    RetryCount: Integer;
    procedure DoSend; override;
    procedure Execute; override;
    procedure Send;
    procedure resend;
    procedure SendNext;
  public
    DownloadTaskID: TGUID;
    FileName: string;
    RealFileName: string;
    StartPos: int64;
    FileSize: int64;
    constructor Create(TaskManager : TPKSTTaskManager; UpLoadManager : TPKSTUpLoadManager);
    destructor Destroy; override;
    procedure DoTimeOut; override;
    procedure ProcReply(var Data; Size: Integer); override;
    function Checktask(CheckID: TGUID): Boolean; override;
  end;

  TPKSTTaskManager = class
  private
    _TaskList: TList;
    _Controller: TPKSTController;
  protected

  public
    constructor Create(Controller: TPKSTController);
    destructor Destroy; override;

    procedure AddTask(Task: TPKSTBasicTask);
    procedure DeleteTask(Task: TPKSTBasicTask);
    function FindTask(TaskId: TGuid): TPKSTBasicTask;
    procedure ProceReply(TaskId: TGuid; var Data; Size: Integer);

  published

  end;


  TPKSTADDDLThread = class(TThread)
  private
    DownloadInfo: TPKSTDownLoadInfo;
    procedure AddDownload;
  protected
    _Manager : TPKSTDownLoadManager;
    procedure Execute; override;
  public
    FileName: string;
    FileHash: TMD5Digest;
    FileSize: Int64;
    Host: string;
    Port: Integer;

    constructor Create(Manager : TPKSTDownLoadManager); virtual;
    destructor Destroy; override;
  published

  end;

  TPKSTDownLoadManager = class
  private
    _Timer         : TTimer;
    _ThreadList    : TList;

    _DownloadQueue : TPKSTQueue;   // 下载队列
    _WaitQueue     : TPKSTQueue;   // 等待队列
    _CompliteQueue : TPKSTQueue;   // 完成队列

    FOnDownloadNotify: TNotifyEvent;
    procedure DoTimeer(Sender: Tobject);
  protected
    _Controller: TPKSTController;

    function GetDownLoadCount: Integer;
    function GetWaitCount: Integer;
    function GetCompliteCount: Integer;

  public
    constructor Create(Controller: TPKSTController);
    destructor Destroy; override;
    procedure AddThread(Thread: TPKSTADDDLThread);
    procedure DeleteThread(Thread: TPKSTADDDLThread);
    procedure AddDownload(Downloadinfo: TPKSTDownLoadInfo); overload;
    procedure AddDownload(FileName: string; Filehash: TMD5Digest; FileSize: Int64; host: string; port: Integer); overload;

    procedure StartDownload;
    procedure FailDownload(DownloadID: TGUID);
    procedure CompleteDownload(DownloadID: TGUID);
    procedure ProceDownloadData(AData: TStream; ABinding: TIdSocketHandle);
    procedure ReplyState(ABinding: TIdSocketHandle; GUID: TGUID; State: Byte; StartPos: Int64); overload;
    procedure ReplyState(DLTaskInfo: TPKSTDLTaskInfo; GUID: TGUID; State: Byte; StartPos: Int64); overload;

    function GetDownloadInfo(Idx: Integer): TPKSTDLTaskInfo;
    function GetWaiteInfo(Idx: Integer): TPKSTDLTaskInfo;
    function GetCompliteInfo(Idx: Integer): TPKSTDLTaskInfo;

    property DownLoadCount: Integer read GetDownLoadCount;
    property WaitCount: Integer read GetWaitCount;
    property CompliteCount : Integer read GetCompliteCount;

    property OnDownloadNotify: TNotifyEvent read FOnDownloadNotify write FOnDownloadNotify;
  published

  end;

  TPKSTADDUPLOADThread = class(TThread)
  private
  protected
    _Manager : TPKSTUpLoadManager;
    procedure SendUpload;
    procedure Execute; override;
  public
    FileName: string;
    RealFileName: string;
    FileHash: TMD5Digest;
    FileSize: Int64;
    StartPos: Int64;
    Host: string;
    Port: Integer;
    DownloadID: TGUID;
    constructor Create(Manager : TPKSTUpLoadManager); virtual;
    destructor Destroy; override;
  published

  end;

  TPKSTUpLoadManager = class
  private
    _ThreadList: TList;
    _TaskList: Tlist;
    function GetUpLoadCount: Integer;
  protected
    _Controller: TPKSTController;
  public
    constructor Create(Controller: TPKSTController);
    destructor Destroy; override;
    procedure AddThread(Thread: TPKSTADDUPLOADThread);
    procedure DeleteThread(Thread: TPKSTADDUPLOADThread);
    procedure AddTask(Task: TPKSTBasicTask);
    procedure DeleteTask(Task: TPKSTBasicTask);
    procedure GetUpLoadInfo(idx: Integer; var FileName: string; Var FileSize, DownloadSize: int64);
    function CanUpLoad:Boolean;
    property UpLoadCount: Integer read GetUpLoadCount;
  end;

implementation

uses Forms, Math, IniFiles, Registry;

{ TPKSTController }

constructor TPKSTController.Create(ServerMode: Boolean);
var
  i: integer;
  Reg: TRegistry;
begin
  ForceDirectories(ExtractFilePath(Application.ExeName)+'TMP');
  Reg := TRegistry.Create;
  Reg.RootKey := HKEY_LOCAL_MACHINE;
  Reg.OpenKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Setup',true);
  UpTime := 0;
  if not Reg.ValueExists('uptime') then
    Reg.WriteCurrency('uptime',0)
  else
    UpTime := Reg.ReadCurrency('uptime');
  Reg.Free;
  Tick := GetTickCount;

  FServerMode := ServerMode;

  _UDPController := TPKSTUDPController.Create(self);
  _UDPController.OnUDPRead := self.DoUDPRead;
  _NodeManager := TPKSTNodeManager.Create(self);
  _TaskManager := TPKSTTaskManager.Create(Self);
  _HashManager := TPKSTHashManager.Create(Self);
  _HashManager.CacheFileName := SlashSep(ExtractFilePath(Application.ExeName),'HashCache.Dat');

  if not FServerMode then
  begin
    _DownloadManager := TPKSTDownLoadManager.Create(Self);
  end;
  _UpLoadManager := TPKSTUpLoadManager.Create(Self);
end;

destructor TPKSTController.Destroy;
begin
  stop;

  SafeFreeAndNil(_DownloadManager);
  SafeFreeAndNil(_UpLoadManager);
  SafeFreeAndNil(_TaskManager);
  SafeFreeAndNil(_NodeManager);
  SafeFreeAndNil(_UDPController);
  SafeFreeAndNil(_HashManager);
  inherited;
end;

procedure TPKSTController.Init(cfgfile: string);
var
  ini: TIniFile;
  S: TstringList;
  i: integer;
  GUID: TGUID;
begin
  if not FileExists(cfgfile) then
  begin
    CreateGUID(GUID);
    S := TstringList.Create;
    s.add('percast.meibu.com');
    Init(GUID,s);
    s.Free;
    Save(cfgfile);
    exit;
  end;
  ini := TIniFile.Create(cfgfile);
  if ini.SectionExists('GUID') then
    FNodeID := StringToGUID(ini.ReadString('GUID','ID',''));
  if ini.SectionExists('NODE') then
    FNodeName := ini.ReadString('NODE','NAME','');
  if ini.SectionExists('CONFIG') then
  begin
    _MaxDownloads := ini.ReadInteger('CONFIG','MAXDOWNLOADS',3);
    _MaxUpLoads := ini.ReadInteger('CONFIG','MAXUPLOADS',3);
    _CRCCheck := ini.ReadBool('CONFIG','CRCCHKECK',False);
    _RcvDir := ini.ReadString('CONFIG','RCVDIR',SlashSep(ExtractFilePath(Application.ExeName),'Download'));
    if ini.ValueExists('CONFIG', 'USECENTER') then
      FUseCenter := ini.ReadBool('CONFIG','USECENTER',False)
    else
      FUseCenter := false;
  end
  else
  begin
    _MaxDownloads := 3;
    _MaxUpLoads := 3;
    _CRCCheck := True;
    _RcvDir := SlashSep(ExtractFilePath(Application.ExeName),'Download');
  end;

  if Not FServerMode then
    ForceDirectories(_RcvDir)
  else
  begin
//    ForceDirectories(SlashSep(ExtractFilePath(Application.ExeName),'Schedule'));
  end;

  if Not FServerMode then
    if ini.SectionExists('SERVERS') then
    begin
      S := TstringList.Create;
      ini.ReadSectionValues('SERVERS', S);
      for i := 0 to s.Count-1 do
        s.Strings[i] := s.ValueFromIndex[i];
      _NodeManager.InitServerList(s);
      S.Free;
    end;
  ini.Free;
end;

procedure TPKSTController.DoUDPRead(Sender: TObject; AData: TStream;
  ABinding: TIdSocketHandle);
var
  PKSTHead: TPKSTHead;
begin
  //如果不是PKST数据包，则抛弃
  if Adata.Size<9 then
    exit;

//  _NodeManager.UpdateConnect(ABinding.PeerIP,ABinding.PeerPort);

  AData.Read(PKSTHead, Sizeof(TPKSTHead));
  If PKSTHead.Flag<>PKSTFLAG then
    exit;

  AData.Seek(0,0);
  case PKSTHead.DataType of
    PKST_CONNECT  : DoConnect(AData, ABinding);   // 连接
    PKST_REPLY    : DoReply(AData, ABinding);     // 应答
    PKST_NODEINFO : DoNodeInfo(AData, ABinding);  // 节点信息
    PKST_NODELIST : DoNodeList(AData, ABinding);  // 节点列表
    PKST_ASKNLIST : DoAskNList(AData, ABinding);  // 请求节点列表
    PKST_PUBLISH  : DoPublish(AData, ABinding);   // 发布信息
    PKST_DOWNLOAD : DoDownload(AData, ABinding);  // 下载请求
    PKST_MSG      : DoMsg(AData, ABinding);       // 消息
    PKST_DATA     : DoData(AData, ABinding);      // 数据
    PKST_SUBTITLE : DoSubTitle(AData, ABinding);      // 数据
  end;
end;

procedure TPKSTController.Init(ID: TGUID; Serverlist: Tstringlist);
var
  computername: array [0..MAX_COMPUTERNAME_LENGTH] of Char;
  namesize: DWORD;
begin
  GetComputerName(@computername, namesize);
  FNodeID := ID;
  FNodeName := computername;

  _MaxUpLoads := 3;
  _CRCCheck := False;
  If FServerMode then exit;
  _MaxDownloads := 3;
  _RcvDir := SlashSep(ExtractFilePath(Application.ExeName),'Download');
  if Not FServerMode then
    ForceDirectories(_RcvDir);
  _NodeManager.InitServerList(Serverlist);
end;

procedure TPKSTController.Open;
var
  i: integer;
  Reg: TRegistry;
begin
  Reg := TRegistry.Create;
  Reg.RootKey := HKEY_LOCAL_MACHINE;
  Reg.OpenKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Setup',true);
  UpTime := UpTime+GetTickCount-Tick;
  Tick := GetTickCount;
  Reg.WriteCurrency('uptime',UpTime);
  Reg.Free;
  if UpTime > 30.0*86400*1000 then
  begin
    raise Exception.Create('软件过期，请与开发商联系');
  end;

  if Date > EncodeDate(2003,11,30) then
    raise Exception.Create('软件过期，请与开发商联系');

  _UDPController.open(ServerMode);
  _NodeManager.Open;
  _HashManager.open;
end;

procedure TPKSTController.Save(cfgfile: string);
var
  ini: TIniFile;
  i: Integer;
  Reg: TRegistry;
begin
  Reg := TRegistry.Create;
  Reg.RootKey := HKEY_LOCAL_MACHINE;
  Reg.OpenKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Setup',true);
  UpTime := UpTime+GetTickCount-Tick;
  Tick := GetTickCount;
  Reg.WriteCurrency('uptime',UpTime);
  Reg.Free;

  ini := TIniFile.Create(cfgfile);
  ini.WriteString('GUID','ID', GUIDToString(FNodeID));
  ini.WriteString('NODE','NAME', FNodeName);
  ini.WriteInteger('CONFIG','MAXDOWNLOADS',_MaxDownloads);
  ini.WriteInteger('CONFIG','MAXUPLOADS',_MaxUpLoads);
  ini.WriteBool('CONFIG','CRCCHKECK', _CRCCheck);
  ini.WriteString('CONFIG','RCVDIR',_RcvDir);
  if ini.ValueExists('CONFIG','USECENTER') or FUseCenter then
    ini.WriteBool('CONFIG','RCVDIR',FUseCenter);

  if ini.SectionExists('SERVERS') then
    ini.EraseSection('SERVERS');
  if Not FServerMode then
    for i := 0 to _NodeManager._ServerList.Count-1 do
      ini.WriteString('SERVERS', inttostr(i), _NodeManager._ServerList[i]);

  ini.Free;
end;

procedure TPKSTController.DoAskNList(AData: TStream;
  ABinding: TIdSocketHandle);
var
  Guid: TGUID;
  PKSTHead: TPKSTHead;
  Data: array of Byte;
  NodeCount: Integer;
  Node: TPKSTNode;
  M: TMemoryStream;
begin
  AData.Read(PKSTHead, Sizeof(TPKSTHead));
  AData.Read(Guid, Sizeof(GUID));

  M := TMemoryStream.Create;
  M.Write(Guid, Sizeof(TGUID));
  M.Seek(0,0);
  setlength(Data, M.size);
  M.Read(Data[0],M.size);
  M.free;
  REPLY(ABinding, Data[0], Length(Data));
  //发送指定server的NodeList
end;

procedure TPKSTController.DoConnect(AData: TStream;
  ABinding: TIdSocketHandle);
var
  data: pchar;
begin
  if FServerMode then
    if Not _NodeManager.CheckActiveNode(ABinding.PeerIP,ABinding.PeerPort) then
      GetNodeInfo(ABinding.PeerIP,ABinding.PeerPort);
  REPLY(ABinding, data, 0);
  _NodeManager.UpdateConnect(ABinding.PeerIP,ABinding.PeerPort);
end;

procedure TPKSTController.DoData(AData: TStream;
  ABinding: TIdSocketHandle);
begin
  _DownloadManager.ProceDownloadData(AData, ABinding);
end;

procedure TPKSTController.DoDownload(AData: TStream;
  ABinding: TIdSocketHandle);
var
  PKSTHead: TPKSTHead;
  RequestID  : TGUID;
  TaskID     : TGUID;
  FileHash   : TMD5Digest;
  FileSize   : Int64;
  StartPos   : Int64;
  FileName   : string;
  Data: array of Char;
  state: Byte;
  UpLoad: TPKSTADDUPLOADThread;
  M: TMemoryStream;
begin
  AData.Read(PKSTHead, Sizeof(TPKSTHead));
  AData.Read(RequestID, Sizeof(TGUID));
  AData.Read(TaskID, Sizeof(TGUID));
  AData.Read(FileHash, Sizeof(TMD5Digest));
  AData.Read(FileSize, Sizeof(Int64));
  AData.Read(StartPos, Sizeof(Int64));
  SetLength(Data, PKSTHead.DataSize-Sizeof(Int64)-Sizeof(TMD5Digest));
  AData.Read(Data[0],Length(Data));
  FileName := string(Pchar(@Data[0]));

  if not _UpLoadManager.CanUpLoad then
  begin
    state := PKST_REQUEST_ERR;
    M := TMemoryStream.Create;
    M.Write(RequestID, Sizeof(TGUID));
    M.Write(state, Sizeof(Byte));

    M.Seek(0,0);
    setlength(Data, M.size);
    M.Read(Data[0],M.size);
    M.free;
    REPLY(ABinding, Data[0], Length(Data));

    exit;
  end;

  state := PKST_REQUEST_OK;
  M := TMemoryStream.Create;
  M.Write(RequestID, Sizeof(TGUID));
  M.Write(state, Sizeof(Byte));

  M.Seek(0,0);
  setlength(Data, M.size);
  M.Read(Data[0],M.size);
  M.free;
  REPLY(ABinding, Data[0], Length(Data));

  UpLoad := TPKSTADDUPLOADThread.Create(_UpLoadManager);
  UpLoad.DownloadID := TaskID;
  UpLoad.FileName := FileName;
  UpLoad.FileHash := FileHash;
  UpLoad.FileSize := FileSize;
  UpLoad.StartPos := StartPos;
  UpLoad.Host := ABinding.PeerIP;
  UpLoad.Port := ABinding.PeerPort;
  UpLoad.Resume;
  SendNodeList(ABinding.PeerIP, ABinding.PeerPort);
end;

procedure TPKSTController.DoNodeInfo(AData: TStream;
  ABinding: TIdSocketHandle);
var
  Guid: TGUID;
  PKSTHead: TPKSTHead;
  Data: array of Byte;
  Size: Integer;
  M: TMemoryStream;
  LocalIP: TIdInAddr;
  tmpNodeID: TGuid;
  tmpNode: TPKSTNode;
  tmpNodeName: string;
  s: TStringStream;
begin
  AData.Read(PKSTHead, Sizeof(TPKSTHead));
  AData.Read(Guid, Sizeof(GUID));

  AData.Read(tmpNodeID, sizeof(TGUID));
  AData.Read(LocalIP, Sizeof(TIdInAddr));
  try
    setlength(Data, PKSTHead.DataSize);
    Size := AData.Read(Data[0], PKSTHead.DataSize);
    S := TStringStream.Create('');
    S.Write(Data[0], Size);
    tmpNodeName := S.DataString;
  finally
    S.Free;
  end;

  tmpNode.IP := GStack.StringToTInAddr(ABinding.PeerIP);
  tmpNode.LocalIP := LocalIP;
  tmpNode.Port := ABinding.PeerPort;
  _NodeManager.ProcNodeInfo(tmpNodeName, tmpNode, tmpNodeID);

  if FServerMode then
  begin
    LocalIP := GStack.StringToTInAddr(GStack.LocalAddress);
    M := TMemoryStream.Create;
    M.Write(Guid, Sizeof(TGUID));
    M.Write(FNodeID, Sizeof(TGUID));
    M.Write(LocalIP, Sizeof(TIdInAddr));
    M.Write(FNodename[1], Length(FNodeName));
    M.Seek(0,0);
    setlength(Data, M.size);
    M.Read(Data[0],M.size);
    M.free;
    REPLY(ABinding, Data[0], Length(Data));
  end
  else
  begin
    LocalIP := GStack.StringToTInAddr(GStack.LocalAddress);
    M := TMemoryStream.Create;
    M.Write(Guid, Sizeof(TGUID));
    M.Write(FNodeID, Sizeof(TGUID));
    M.Write(LocalIP, Sizeof(TIdInAddr));
    M.Write(FNodename[1], Length(FNodeName));
    M.Seek(0,0);
    setlength(Data, M.size);
    M.Read(Data[0],M.size);
    M.free;
    REPLY(ABinding, Data[0], Length(Data));
  end;
end;

procedure TPKSTController.DoNodeList(AData: TStream;
  ABinding: TIdSocketHandle);
var
  Guid: TGUID;
  PKSTHead: TPKSTHead;
  Data: array of Byte;
  NodeCount: Integer;
  Reserve1, Reserve2, Reserve3: Integer;
  Node: TPKSTNode;
  M: TMemoryStream;
begin
  AData.Read(PKSTHead, Sizeof(TPKSTHead));
  AData.Read(Guid, Sizeof(GUID));
  AData.Read(NodeCount, Sizeof(Integer));
  AData.Read(Reserve1, Sizeof(Integer));
  AData.Read(Reserve1, Sizeof(Integer));
  AData.Read(Reserve1, Sizeof(Integer));

  M := TMemoryStream.Create;
  M.Write(Guid, Sizeof(TGUID));
  M.Seek(0,0);
  setlength(Data, M.size);
  M.Read(Data[0],M.size);
  M.free;
  REPLY(ABinding, Data[0], Length(Data));

  if FServerMode then
    exit;

  while not (Adata.Position>= Adata.Size) do
  begin
    AData.Read(Node, Sizeof(TPKSTNode));
    CheckInServerNodes(ABinding.PeerIP, Node);
  end;
end;

procedure TPKSTController.DoPublish(AData: TStream;
  ABinding: TIdSocketHandle);
var
  Guid: TGUID;
  PKSTHead: TPKSTHead;
  FileSize: Int64;
  FileHash: TMD5Digest;
  FileName: string;
  Data: array of Char;
  M: TMemoryStream;
begin
  AData.Read(PKSTHead, Sizeof(TPKSTHead));
  AData.Read(Guid, Sizeof(GUID));
  AData.Read(FileSize, Sizeof(Int64));
  AData.Read(FileHash, Sizeof(TMD5Digest));
  SetLength(Data, PKSTHead.DataSize-Sizeof(Int64)-Sizeof(TMD5Digest));
  AData.Read(Data[0],Length(Data));
  FileName := string(Pchar(@Data[0]));
  _DownloadManager.AddDownload(FileName,FileHash,FileSize, ABinding.PeerIP, ABinding.PeerPort);

  M := TMemoryStream.Create;
  M.Write(Guid, Sizeof(TGUID));
  M.Seek(0,0);
  setlength(Data, M.size);
  M.Read(Data[0],M.size);
  M.free;
  REPLY(ABinding, Data[0], Length(Data));
end;

procedure TPKSTController.DoReply(AData: TStream;
  ABinding: TIdSocketHandle);
var
  PKSTHead: TPKSTHead;
  Data: array of Byte;
begin
  AData.Read(PKSTHead, Sizeof(TPKSTHead));
  if PKSTHead.DataSize = 0 then
  begin
    _NodeManager.UpdateConnect(ABinding.PeerIP,ABinding.PeerPort);
    // 连接应答，无动作。
  end
  else
  begin
    Setlength(Data, PKSTHead.DataSize);
    AData.Read(Data[0], PKSTHead.DataSize);
    _TaskManager.ProceReply(PGUID(@Data[0])^, Data[0], PKSTHead.DataSize);
  end;
end;

procedure TPKSTController.REPLY(ABinding: TIdSocketHandle; var Data;
  Size: Integer);
var
  PKSTHead: TPKSTHead;
  Buf: array of Byte;
  M: TMemoryStream;
begin
  PKSTHead.Flag := PKSTFLAG;
  PKSTHead.Version := PKSTVERSION;
  PKSTHead.DataType := PKST_REPLY;
  PKSTHead.DataSize := Size;
  M := TMemoryStream.Create;
  M.Write(PKSTHead, sizeof(TPKSTHead));
  M.Write(Data, size);
  Size := M.Size;
  setlength(Buf, Size);
  M.Seek(0,0);
  M.Read(Buf[0], Size);
  M.Free;
  _UDPController.Send(ABinding.PeerIP, ABinding.PeerPort, Buf[0], Size);
end;

procedure TPKSTController.SendConnect(AHost: string; Port: Integer);
var
  PKSTHead: TPKSTHead;
  Reg: TRegistry;
begin
  Reg := TRegistry.Create;
  Reg.RootKey := HKEY_LOCAL_MACHINE;
  Reg.OpenKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Setup',true);
  UpTime := UpTime+GetTickCount-Tick;
  Tick := GetTickCount;
  Reg.WriteCurrency('uptime',UpTime);
  Reg.Free;
  if UpTime > 30.0*86400*1000 then
  begin
    Stop;
    raise Exception.Create('软件过期，请与开发商联系');
  end;

  PKSTHead.Flag := PKSTFLAG;
  PKSTHead.Version := PKSTVERSION;
  PKSTHead.DataType := PKST_CONNECT;
  PKSTHead.DataSize := 0;
  _UDPController.Send(AHost, Port, PKSTHead, Sizeof(TPKSTHead));
end;
{
procedure TPKSTController.DisConnect(AHost: string; Port: Integer);
var
  i,j: Integer;
  NodeID: TGUID;
begin
  if FServerMode then
  begin        
    for i:= FNodeCount-1 downto 0 do
    begin
      if (AHost = GStack.TInAddrToString(FNodes[i].NodeAdd.IP)) and (Port = FNodes[i].NodeAdd.Port) then
      begin
        if i<FNodeCount-1 then
          Move(FNodes[i+1], FNodes[i], (FNodeCount-i-1)*sizeof(TPKSTNodeInfo));
        FNodeCount := FNodeCount-1;
      end;
    end;
  end
  else
  begin
    for i:= 0 to FServerNodeCount-1 do
      for j:= FServerNodes[i].NodeCount-1 downto 0 do
      begin
        if (AHost = GStack.TInAddrToString(FServerNodes[i].Nodes[j].NodeAdd.IP)) and (Port = FServerNodes[i].Nodes[j].NodeAdd.Port) then
        begin
          if j<FServerNodes[i].NodeCount-1 then
            Move(FServerNodes[i].Nodes[j+1], FServerNodes[i].Nodes[j], (FServerNodes[i].NodeCount-j-1)*sizeof(TPKSTNodeInfo));
          FServerNodes[i].NodeCount := FServerNodes[i].NodeCount-1;
        end;
      end;
  end;
  if Assigned(self.FonDisconnect) then
    self.FonDisconnect(Self, AHost, Port); 
end;                                       {}
{
procedure TPKSTController.Connect(AHost: string; Port: Integer);
begin
  if FServerMode then
  begin

  end
  else
  begin

  end;
{  if Pos('.', AHost)=0 then
    AHost := AHost+'.meibu.com';{}
{  _NodeManager.Connect(AHost,Port);
end;
{}
procedure TPKSTController.Stop;
begin
  _UDPController.Stop;
  _NodeManager.Stop;
end;

function TPKSTController.GetNodes(I: Integer): TPKSTNodeInfo;
begin
//  Result := FNodeS[i];
end;

procedure TPKSTController.GetNodeInfo(AHost: string; Port: Integer);
var
  Task: TPKSTNodeInfoTask;
  PKSTHead: TPKSTHead;
  Guid: TGUID;
  M: TMemoryStream;
  LocalIP: TIdInAddr;
begin
  LocalIP := GStack.StringToTInAddr(GStack.LocalAddress);
  CreateGUID(Guid);
  Task := TPKSTNodeInfoTask.Create(_TaskManager);
  Task.LastResponseTime := Now;
  Task.TimeOut := PKST_DATA_TIMEOUT/86400;
  Task.TaskID := Guid;
  Task.Host := AHost;
  Task.Port := Port;
  PKSTHead.Flag := PKSTFLAG;
  PKSTHead.Version := PKSTVERSION;
  PKSTHead.DataType := PKST_NODEINFO;
  PKSTHead.DataSize := Sizeof(TGUID);
  M := TMemoryStream.Create;
  M.Write(PKSTHead, Sizeof(TPKSTHead));
  M.Write(Guid, Sizeof(TGUID));
  M.Write(FNodeID, Sizeof(TGUID));
  M.Write(LocalIP, Sizeof(TIdInAddr));
  M.Write(FNodename[1], Length(FNodeName));
  m.Seek(0,0);
  setlength(task.SendData, M.Size);
  M.Read(task.SendData[0],M.Size);
  task.SendSize := M.size;
  M.Free;
  task.Resume;
end;


procedure TPKSTController.SendNodeList(AHost: string; Port: Integer);
var
  Task: TPKSTSendNlistTask;
  PKSTHead: TPKSTHead;
  Guid: TGUID;
  Reserve1, Reserve2, Reserve3: Integer;
  i: Integer;
  M: TMemoryStream;
  IP: TIdInAddr;
  Node: TPKSTNode;
begin
  IP := GStack.StringToTInAddr(AHost);
  Reserve1 := 0;
  Reserve2 := 0;
  Reserve3 := 0;
  CreateGUID(Guid);
  Task := TPKSTSendNlistTask.Create(_TaskManager);
  Task.LastResponseTime := Now;
  Task.TimeOut := PKST_DATA_TIMEOUT/86400;
  Task.TaskID := Guid;
  Task.Host := AHost;
  Task.Port := Port;
  PKSTHead.Flag := PKSTFLAG;
  PKSTHead.Version := PKSTVERSION;
  PKSTHead.DataType := PKST_NODELIST;
  M := TMemoryStream.Create;
  M.Write(PKSTHead, Sizeof(TPKSTHead));
  M.Write(Guid, Sizeof(TGUID));
  M.Write(_NodeManager.ActiveNodes.Count, Sizeof(Integer));
  M.Write(Reserve1, Sizeof(Integer));
  M.Write(Reserve1, Sizeof(Integer));
  M.Write(Reserve1, Sizeof(Integer));
  for i := _NodeManager.ActiveNodes.Count-1 downto 0 do
  begin
    Node := TPKSTNodeInfo(_NodeManager.ActiveNodes.Items[i]).NodeAdd;
    if ((Node.IP.S_addr=IP.S_addr) and (Node.Port = Port)) or
       ((Node.LocalIP.S_addr = IP.S_addr) and (Node.Port = PKST_CLIENTPORT))then
      continue;
    M.Write(Node, Sizeof(TPKSTNode));
  end;
  PKSTHead.DataSize := M.Size-Sizeof(TPKSTHead);
  m.Seek(0,0);
  M.Write(PKSTHead, Sizeof(TPKSTHead));
  m.Seek(0,0);
  setlength(task.SendData, M.Size);
  M.Read(task.sendData[0],M.Size);
  task.SendSize := M.size;
  M.Free;
  task.Resume;
end;

procedure TPKSTController.CheckInServerNodes(ServerHost: string;
  Node: TPKSTNode);
var
  i,j: Integer;
  NodeInfo: TPKSTNodeInfo;
  NodeInfoEX: TPKSTNodeInfoEX;
begin
  if FServerMode then
    exit;

  NodeInfoEX := _NodeManager.GetActiveServerByHost(ServerHost);
  if NodeInfoEX=nil then
    NodeInfoEX := _NodeManager.GetDeActiveServerByHost(ServerHost);
  if NodeInfoEX=nil then
    exit;
  for i := NodeInfoEX.Nodes.Count-1 downto 0 do
  begin
    NodeInfo := TPKSTNodeInfo(NodeInfoEX.Nodes.Items[i]);
    if (NodeInfo.NodeAdd.IP.S_addr = Node.IP.S_addr) and (NodeInfo.NodeAdd.Port = Node.Port) then
    begin
      NodeInfo.NodeAdd.LocalIP := Node.LocalIP;
      exit;
    end;
  end;

  NodeInfo := TPKSTNodeInfo.Create;
  NodeInfo.NodeAdd := Node;
  NodeInfoEX.Nodes.Add(NodeInfo);
end;

procedure TPKSTController.ASKNodeList(AHost: string; Port: Integer;
  ServerHost: string);
var
  Task: TPKSTAskNListTask;
  PKSTHead: TPKSTHead;
  Guid: TGUID;
  M: TMemoryStream;
begin
  CreateGUID(Guid);
  Task := TPKSTAskNListTask.Create(_TaskManager);
  Task.LastResponseTime := Now;
  Task.TimeOut := PKST_DATA_TIMEOUT/86400;
  Task.TaskID := Guid;
  Task.Host := AHost;
  Task.Port := Port;
  PKSTHead.Flag := PKSTFLAG;
  PKSTHead.Version := PKSTVERSION;
  PKSTHead.DataType := PKST_ASKNLIST;
  PKSTHead.DataSize := Sizeof(TGUID);
  M := TMemoryStream.Create;
  M.Write(PKSTHead, Sizeof(TPKSTHead));
  M.Write(Guid, Sizeof(TGUID));
  m.Seek(0,0);
  setlength(Task.SendData, M.Size);
  M.Read(Task.SendData[0],M.Size);
  Task.SendSize := M.size;
  M.Free;
  task.Resume;
end;

procedure TPKSTController.Publish(FileNames: TStrings;
  Nodes: array of TPKSTNode; Pubenvent,Endenvent: TNotifyEvent);
var
  Task: TPKSTPublishTask;
  i: Integer;
begin
  Task := TPKSTPublishTask.Create(_TaskManager);
  Task.LastResponseTime := Now;
  Task.TimeOut := PKST_CONN_TIMEOUT/86400;
  setlength(Task.PubDatas, FileNames.Count);
  for i:= 0 to FileNames.Count-1 do
    Task.PubDatas[i].FileName := FileNames[i];
  setlength(Task.PubNodes, Length(Nodes));
  for i:= 0 to High(Nodes) do
    Task.PubNodes[i] := Nodes[i];
  Task.OnPublish := Pubenvent;
  Task.OnEndPublish := Endenvent;
  task.Resume;
end;

procedure TPKSTController.Download(AHost: string; Port: Integer; DownloadID: TGUID; FileHash: TMD5Digest;
  FileSize: Int64; FileName: string; start: int64);
var
  Task: TPKSTDownloadTask;
  PKSTHead: TPKSTHead;
  Guid: TGUID;
  M: TMemoryStream;
begin
  CreateGUID(Guid);
  Task := TPKSTDownloadTask.Create(_TaskManager);
  Task.TaskID := Guid;
  Task.LastResponseTime := Now;
  Task.TimeOut := PKST_CONN_TIMEOUT/86400;
  Task.DownloadTaskID := DownloadID;
  Task.Host := AHost;
  task.Port := Port;

  PKSTHead.Flag := PKSTFLAG;
  PKSTHead.Version := PKSTVERSION;
  PKSTHead.DataType := PKST_DOWNLOAD;
  M := TMemoryStream.Create;
  M.Write(PKSTHead, Sizeof(TPKSTHead));
  M.Write(Guid, Sizeof(TGUID));
  M.write(DownloadID, Sizeof(TGUID));
  M.write(FileHash, Sizeof(TMD5Digest));
  M.write(FileSize, Sizeof(Int64));
  M.write(Start, Sizeof(Int64));
  M.write(FileName[1], length(FileName));

  PKSTHead.DataSize := M.Size-Sizeof(TPKSTHead);
  m.Seek(0,0);
  M.Write(PKSTHead, Sizeof(TPKSTHead));
  m.Seek(0,0);
  setlength(Task.SendData, M.Size);
  M.Read(Task.SendData[0],M.Size);
  Task.SendSize := M.size;
  M.Free;
  task.Resume;
end;

procedure TPKSTController.SendData(AHost: string; Port: Integer; DownloadID: TGUID; FileHash: TMD5Digest;
  FileSize: Int64; FileName: string; start: int64; RealFileName: string);
var
  Task: TPKSTDataTask;
begin
  Task := TPKSTDataTask.Create(_TaskManager, _UpLoadManager);
  Task.LastResponseTime := Now;
  Task.TimeOut := PKST_CONN_TIMEOUT/86400;
  Task.DownloadTaskID := DownloadID;
  task.FileName := FileName;
  if RealFileName='' then
    task.realFileName := FileName
  else
    task.realFileName := RealFileName;
  task.FileSize := FileSize;
  task.StartPos := start;
  Task.Host := AHost;
  task.Port := Port;
  task.Resume;
end;

procedure TPKSTController.SetDownloadNotify(Value: TNotifyEvent);
begin
  _DownloadManager.OnDownloadNotify := Value;
end;

function TPKSTController.GetServerAdd(I: Integer): string;
begin
  try
    Result := _NodeManager._ServerList[i];
  except
    Result := '';
  end;   {}
end;

function TPKSTController.GetServerCount: Integer;
begin
  Result := _NodeManager._ServerList.Count;
{  Result := FServerList.Count; {}
end;

procedure TPKSTController.InitServers(Serverlist: Tstrings);
begin
  _NodeManager.InitServerList(Serverlist);
//  FServerList.Clear;
//  for i:= 0 to Serverlist.Count-1 do
//{    if Pos('.',Serverlist[i])=0 then
//      FServerList.Add(Serverlist[i]+'.meibu.com')
//    else {}
//      FServerList.Add(Serverlist[i])
////  FServerList.Assign(Serverlist);
end;

procedure TPKSTController.SetMaxDownloads(Value: Integer);
begin
  if _MaxDownloads<>Value then
    _MaxDownloads := Value;
  if _MaxDownloads>10 then
    _MaxDownloads := 10;
end;

procedure TPKSTController.SetMaxUpLoads(Value: Integer);
begin
  if _MaxUploads<>Value then
    _MaxUploads := Value;
  if _MaxUploads>10 then
    _MaxUploads := 10;
end;

procedure TPKSTController.Download(Node: TPKSTNode; DownloadID: TGUID; FileHash: TMD5Digest;
  FileSize: Int64; FileName: string; start: int64);
var
  Task: TPKSTDownloadTask;
  PKSTHead: TPKSTHead;
  Guid: TGUID;
  M: TMemoryStream;
begin
  CreateGUID(Guid);
  Task := TPKSTDownloadTask.Create(_TaskManager);
  Task.TaskID := Guid;
  Task.LastResponseTime := Now;
  Task.TimeOut := PKST_CONN_TIMEOUT/86400;
  Task.DownloadTaskID := DownloadID;
  Task.Node := Node;
  if Node.LocalIP.S_addr<>0 then
  begin
    Task.Host := GStack.TInAddrToString(Node.LocalIP);
    Task.Port := PKST_CLIENTPORT;
  end
  else
  begin
    Task.Host := GStack.TInAddrToString(Node.IP);
    Task.Port := Node.Port;
  end;

  PKSTHead.Flag := PKSTFLAG;
  PKSTHead.Version := PKSTVERSION;
  PKSTHead.DataType := PKST_DOWNLOAD;
  M := TMemoryStream.Create;
  M.Write(PKSTHead, Sizeof(TPKSTHead));
  M.Write(Guid, Sizeof(TGUID));
  M.write(DownloadID, Sizeof(TGUID));
  M.write(FileHash, Sizeof(TMD5Digest));
  M.write(FileSize, Sizeof(Int64));
  M.write(Start, Sizeof(Int64));
  M.write(FileName[1], length(FileName));

  PKSTHead.DataSize := M.Size-Sizeof(TPKSTHead);
  m.Seek(0,0);
  M.Write(PKSTHead, Sizeof(TPKSTHead));
  m.Seek(0,0);
  setlength(Task.SendData, M.Size);
  M.Read(Task.SendData[0],M.Size);
  Task.SendSize := M.size;
  M.Free;
  task.Resume;
end;

function TPKSTController.SendMsg(Node: TPKSTNode;
  const Msg: string): integer;
var
  Task: TPKSTMsgTask;
  PKSTHead: TPKSTHead;
  Guid: TGUID;
  M: TMemoryStream;
begin
  CreateGUID(Guid);
  Task := TPKSTMsgTask.Create(_TaskManager);
  Task.LastResponseTime := Now;
  Task.TimeOut := PKST_CONN_TIMEOUT/86400;
  Task.TaskID := Guid;
  Task.Node := Node;
  if Node.LocalIP.S_addr<>0 then
  begin
    Task.Host := Gstack.TInAddrToString(Node.LocalIP);
    Task.Port := PKST_CLIENTPORT;
  end
  else
  begin
    Task.Host := Gstack.TInAddrToString(Node.IP);
    Task.Port := Node.Port;
  end;

  PKSTHead.Flag := PKSTFLAG;
  PKSTHead.Version := PKSTVERSION;
  PKSTHead.DataType := PKST_MSG;
  PKSTHead.DataSize := Sizeof(TGUID)+length(Msg);

  M := TMemoryStream.Create;
  M.Write(PKSTHead, Sizeof(TPKSTHead));
  M.Write(Guid, Sizeof(TGUID));
  M.Write(Msg[1], length(Msg));
  m.Seek(0,0);
  setlength(task.SendData, M.Size);
  M.Read(task.SendData[0],M.Size);
  task.SendSize := M.size;
  M.Free;
  task.Resume;
end;

procedure TPKSTController.DoMSg(AData: TStream; ABinding: TIdSocketHandle);
var
  Guid: TGUID;
  PKSTHead: TPKSTHead;
  Data: array of Byte;
  msg: string;
  M: TMemoryStream;
  s: TStringStream;
  Node: string;
  IP: TIdInAddr;
  i: integer;
  NodeInfo: TPKSTNodeInfo;
  NodeInfoEX: TPKSTNodeInfoEX;
begin
  AData.Read(PKSTHead, Sizeof(TPKSTHead));
  AData.Read(Guid, Sizeof(GUID));
  setlength(Data, PKSTHead.DataSize-Sizeof(GUID));
  AData.Read(Data[0], PKSTHead.DataSize-Sizeof(GUID));
  S := TStringStream.Create('');
  s.Write(Data[0],PKSTHead.DataSize-Sizeof(GUID));
  msg := s.DataString;
  s.Free;

  M := TMemoryStream.Create;
  M.Write(Guid, Sizeof(TGUID));
  M.Seek(0,0);
  setlength(Data, M.size);
  M.Read(Data[0],M.size);
  M.free;
  REPLY(ABinding, Data[0], Length(Data));

  IP := GStack.StringToTInAddr(ABinding.PeerIP);
  Node := '';
  if FServerMode then
  begin
    NodeInfo := _NodeManager.GetActiveNodeByHost(ABinding.PeerIP, ABinding.PeerPort);
    if NodeInfo = nil then
      NodeInfo := _NodeManager.GetDeActiveNodeByHost(ABinding.PeerIP, ABinding.PeerPort);
    if NodeInfo = nil then
      exit;
    Node := GUIDToString(NodeInfo.NodeID);
  end
  else
  begin
    NodeInfoEx := _NodeManager.GetActiveServerByHost(ABinding.PeerIP, ABinding.PeerPort);
    if NodeInfoEx = nil then
      NodeInfoEx := _NodeManager.GetDeActiveServerByHost(ABinding.PeerIP, ABinding.PeerPort);
    if NodeInfoEx = nil then
      exit;
    Node := GUIDToString(NodeInfoEX.ServerID);
  end;
  if Node='' then
    Node := ABinding.PeerIP+':'+inttostr(ABinding.PeerPort);

  if assigned(msgproc) then
    msgproc(Node, msg);                   {}
end;

function TPKSTController.GetActiveNode(i: Integer): TPKSTNodeInfo;
begin
  Result := _NodeManager.ActiveNode[i];
end;

function TPKSTController.GetActiveNodeCount: Integer;
begin
  Result := _NodeManager.ActiveNodeCount;
end;

function TPKSTController.GetActiveServer(i: Integer): TPKSTNodeInfoEX;
begin
  Result := _NodeManager.ActiveServer[i];
end;

function TPKSTController.GetActiveServerCount: Integer;
begin
  Result := _NodeManager.ActiveServerCount;
end;

function TPKSTController.GetDeActiveNode(i: Integer): TPKSTNodeInfo;
begin
  Result := _NodeManager.DeActiveNode[i];
end;

function TPKSTController.GetDeActiveNodeCount: Integer;
begin
  Result := _NodeManager.DeActiveNodeCount;
end;

function TPKSTController.GetDeActiveServer(i: Integer): TPKSTNodeInfoEX;
begin
  Result := _NodeManager.DeActiveServer[i];
end;

function TPKSTController.GetDeActiveServerCount: Integer;
begin
  Result := _NodeManager.DeActiveServerCount;
end;

function TPKSTController.SendMsgServer(Node: TPKSTNode;
  const Msg: string): integer;
var
  Task: TPKSTMsgTask;
  PKSTHead: TPKSTHead;
  Guid: TGUID;
  M: TMemoryStream;
begin
  CreateGUID(Guid);
  Task := TPKSTMsgTask.Create(_TaskManager);
  Task.LastResponseTime := Now;
  Task.TimeOut := PKST_CONN_TIMEOUT/86400;
  Task.TaskID := Guid;
  Task.Node := Node;
  if Node.LocalIP.S_addr<>0 then
  begin
    Task.Host := Gstack.TInAddrToString(Node.LocalIP);
    Task.Port := PKST_SERVERPORT;
  end
  else
  begin
    Task.Host := Gstack.TInAddrToString(Node.IP);
    Task.Port := Node.Port;
  end;

  PKSTHead.Flag := PKSTFLAG;
  PKSTHead.Version := PKSTVERSION;
  PKSTHead.DataType := PKST_MSG;
  PKSTHead.DataSize := Sizeof(TGUID)+length(Msg);

  M := TMemoryStream.Create;
  M.Write(PKSTHead, Sizeof(TPKSTHead));
  M.Write(Guid, Sizeof(TGUID));
  M.Write(Msg[1], length(Msg));
  m.Seek(0,0);
  setlength(task.SendData, M.Size);
  M.Read(task.SendData[0],M.Size);
  task.SendSize := M.size;
  M.Free;
  task.Resume;
end;

procedure TPKSTController.REPLY(Host: string; Port: Integer; var Data;
  Size: Integer);
var
  PKSTHead: TPKSTHead;
  Buf: array of Byte;
  M: TMemoryStream;
begin
  PKSTHead.Flag := PKSTFLAG;
  PKSTHead.Version := PKSTVERSION;
  PKSTHead.DataType := PKST_REPLY;
  PKSTHead.DataSize := Size;
  M := TMemoryStream.Create;
  M.Write(PKSTHead, sizeof(TPKSTHead));
  M.Write(Data, size);
  Size := M.Size;
  setlength(Buf, Size);
  M.Seek(0,0);
  M.Read(Buf[0], Size);
  M.Free;
  _UDPController.Send(host, Port, Buf[0], Size);
end;

procedure TPKSTController.PublishSubTitle(Msg: string;
  Nodes: array of TPKSTNode; Pubenvent, Endenvent: TNotifyEvent);
var
  Task: TPKSTPublishSubTitleTask;
  i: Integer;
begin
  Task := TPKSTPublishSubTitleTask.Create(_TaskManager);
  Task.LastResponseTime := Now;
  Task.TimeOut := PKST_CONN_TIMEOUT/86400;
  if pos('MSG:', Msg)=1 then
    Msg := copy(Msg,5,length(Msg));
  Task.SubTitle := Msg;
  setlength(Task.PubNodes, Length(Nodes));
  for i:= 0 to High(Nodes) do
    Task.PubNodes[i] := Nodes[i];
  Task.OnPublish := Pubenvent;
  Task.OnEndPublish := Endenvent;
  task.Resume;
end;

procedure TPKSTController.DoSubTitle(AData: TStream;
  ABinding: TIdSocketHandle);
var
  Guid: TGUID;
  PKSTHead: TPKSTHead;
  Data: array of Byte;
  msg: string;
  M: TMemoryStream;
  s: TStringStream;
begin
  AData.Read(PKSTHead, Sizeof(TPKSTHead));
  AData.Read(Guid, Sizeof(GUID));
  setlength(Data, PKSTHead.DataSize-Sizeof(GUID));
  AData.Read(Data[0], PKSTHead.DataSize-Sizeof(GUID));
  S := TStringStream.Create('');
  s.Write(Data[0],PKSTHead.DataSize-Sizeof(GUID));
  msg := s.DataString;
  s.Free;

  M := TMemoryStream.Create;
  M.Write(Guid, Sizeof(TGUID));
  M.Seek(0,0);
  setlength(Data, M.size);
  M.Read(Data[0],M.size);
  M.free;
  REPLY(ABinding, Data[0], Length(Data));

  if assigned(FOnSubTitle) then
    FOnSubTitle(msg);                   {}
end;

{ TPKSTUDPController }

constructor TPKSTUDPController.Create(Controller: TPKSTController);
begin
  _Controller := Controller;
  _server := TIdUDPServer.Create(nil);
  _server.BufferSize := 65536;
//  _server.ThreadedEvent := true;
  _server.OnUDPRead := FOnUDPRead;
end;

destructor TPKSTUDPController.Destroy;
begin
  if _server<> nil then
    FreeAndNil(_server);
  inherited;
end;

procedure TPKSTUDPController.open(Servermode: Boolean);
begin
  if Date > EncodeDate(2003,11,30) then
    raise Exception.Create('软件过期，请与开发商联系');
  _server.OnUDPRead := _Controller.DoUDPRead;
  if Servermode then
    _server.DefaultPort := PKST_SERVERPORT
  else
    _server.DefaultPort := PKST_CLIENTPORT;
  _server.Active := True;
end;

procedure TPKSTUDPController.Send(Host: string; Port: Integer; var Data;
  Size: Integer);
begin
  try
    host := ResolveHost(host);
    if host<>'' then
      _server.SendBuffer(host, port, Data, size);
  except
  end;
end;

procedure TPKSTUDPController.Stop;
begin
  _server.Active := False;
end;

{ TPKSTTaskManager }

procedure TPKSTTaskManager.AddTask(Task: TPKSTBasicTask);
begin
  _TaskList.Add(Task);
end;

constructor TPKSTTaskManager.Create(Controller: TPKSTController);
begin
  _Controller :=  Controller;
  _TaskList := TList.Create;
end;

procedure TPKSTTaskManager.DeleteTask(Task: TPKSTBasicTask);
var
  i:  Integer;
begin
  i := _TaskList.IndexOf(Task);
  if i>=0 then
    _TaskList.Delete(i);
end;

destructor TPKSTTaskManager.Destroy;
var
  i: integer;
begin
  for i:= _TaskList.Count-1 downto 0 do
    TPKSTBasicTask(_TaskList.Items[i]).Terminate;
  _TaskList.Free;
end;

function TPKSTTaskManager.FindTask(TaskId: TGuid): TPKSTBasicTask;
var
  i: Integer;
begin
  Result := nil;
  For i:= _TaskList.Count-1 downto 0 do
    if TPKSTBasicTask(_TaskList.Items[i]).Checktask(TaskId) then
    begin
      Result := TPKSTBasicTask(_TaskList.Items[i]);
      exit;
    end;
end;

procedure TPKSTTaskManager.ProceReply(TaskId: TGuid; var Data;
  Size: Integer);
begin
  if FindTask(TaskId)<> nil then
    FindTask(TaskId).ProcReply(Data,Size);
end;

{ TPKSTBasicTask }

function TPKSTBasicTask.Checktask(CheckID: TGUID): Boolean;
begin
  result := False;
  if IsEqualGUID(CheckID, TaskID) then
    Result := True;
end;

constructor TPKSTBasicTask.Create(Manager: TPKSTTaskManager);
begin
  inherited Create(True);
  FreeOnTerminate := True;
  _Manager := Manager;
  TimeOut := PKST_DATA_TIMEOUT;
  TimeOutCount := 0;
  Manager.addTask(Self);
  Node.IP.S_addr := 0;
  Node.LocalIP.S_addr := 0;
  Node.Port := 0;
end;

destructor TPKSTBasicTask.Destroy;
begin
  _Manager.DeleteTask(Self);
  inherited;
end;

procedure TPKSTBasicTask.DoSend;
begin
  if SendSize>0 then
    _Manager._Controller._UDPController.Send(Host, Port, SendData[0], SendSize);
end;

procedure TPKSTBasicTask.Execute;
begin
  inherited;
  Synchronize(DoSend);
  while not Terminated do
  begin
    if Now-LastResponseTime > TimeOut then
      DoTimeOut;
    Sleep(550);  
  end;
end;

procedure TPKSTBasicTask.ProcReply(var Data; Size: Integer);
begin
  LastResponseTime := Now;
  if assigned(FonReply) then
    FonReply(self);
end;

{ TPKSTNodeInfoTask }

constructor TPKSTNodeInfoTask.Create(Manager: TPKSTTaskManager);
begin
  inherited;

end;

procedure TPKSTNodeInfoTask.DoTimeOut;
begin
  inherited;
  Terminate;
end;

procedure TPKSTNodeInfoTask.ProcReply(var Data; Size: Integer);
var
  Guid: TGUID;
  LocalIP: TIdInAddr;
  NodeName: String;
  Node: TPKSTNode;
  m: TMemoryStream;
  Buf: array of Byte;
  BufSize: Integer;
  s: TStringStream;
begin
  inherited;
  LastResponseTime := Now;
  if Size>=Sizeof(TGUID) then
  begin
    m := TMemoryStream.Create;
    S := TStringStream.Create('');
    try
      m.Write(Data,size);
      m.Seek(sizeof(TGUID),0);
      m.Read(Guid, sizeof(TGUID));
      m.Read(LocalIP, Sizeof(TIdInAddr));
      setlength(Buf, m.Size);
      BufSize := m.Read(Buf[0], m.Size);
      S.Write(Buf[0], BufSize);
      NodeName := S.DataString;
    finally
      M.Free;
      S.Free;
    end;
    Node.IP := GStack.StringToTInAddr(host);
    Node.LocalIP := LocalIP;
    Node.Port := Port;
    _Manager._Controller._NodeManager.ProcNodeInfo(NodeName, Node, GUID);
    _Manager._Controller.SendNodeList(Host, Port);
  end;
  Terminate;
end;

{ TPKSTSendNListTask }

constructor TPKSTSendNListTask.Create(Manager: TPKSTTaskManager);
begin
  inherited;

end;

procedure TPKSTSendNListTask.DoTimeOut;
begin
  inherited;
  Terminate;
end;

procedure TPKSTSendNListTask.ProcReply(var Data; Size: Integer);
begin
  inherited;
  Terminate;
end;

{ TPKSTAskNListTask }

constructor TPKSTAskNListTask.Create(Manager: TPKSTTaskManager);
begin
  inherited;

end;

procedure TPKSTAskNListTask.DoTimeOut;
begin
  inherited;
  if Node.LocalIP.S_addr= GStack.StringToTInAddr(Host).S_addr then
  begin
    Host := GStack.TInAddrToString(Node.IP);
    Port := Node.Port;
    DoSend;
  end
  else
    Terminate;
end;

procedure TPKSTAskNListTask.ProcReply(var Data; Size: Integer);
begin
  inherited;
  Terminate;
end;

{ TPKSTPublishTask }

function TPKSTPublishTask.Checktask(CheckID: TGUID): Boolean;
begin
  result:= inherited Checktask(CheckID);
end;

constructor TPKSTPublishTask.Create(Manager: TPKSTTaskManager);
begin
  inherited;
  CurData := 0;
  CurNode := 0;
end;

procedure TPKSTPublishTask.DoTimeOut;
begin
  inherited;
  PublishNext;
end;

procedure TPKSTPublishTask.Execute;
begin
  prepare;
  Synchronize(Publish);
  while not Terminated do
  begin
    if Now-LastResponseTime > TimeOut then
      DoTimeOut;
    Sleep(550);
  end;
end;

procedure TPKSTPublishTask.prepare;
var
  i: Integer;
  Hi,Lo: Cardinal;
  HFile: THandle;
  fn: string;
begin
  lo := 0;
  Hi := 0;
  for i:= 0 to high(PubDatas) do
  begin
    fn := PubDatas[i].FileName;
    if Pos('SCH://', fn)=1 then
      fn := copy(fn,7,length(fn));
    HFile := FileOpen(fn, fmOpenRead or fmShareDenyWrite);
    if HFile <> INVALID_HANDLE_VALUE then
      lo := GetFileSize(HFile, @Hi)
    else
    begin
      FileClose(HFile);
      PubDatas[i].FileSize.Lo := 0;
      PubDatas[i].FileSize.Hi := 0;
      Continue;
    end;
    if (lo=$FFFFFFFF) and (GetLastError<>NO_ERROR) then
    begin
      FileClose(HFile);
      PubDatas[i].FileSize.Lo := 0;
      PubDatas[i].FileSize.Hi := 0;
      Continue;
    end;
    FileClose(HFile);
    PubDatas[i].FileSize.Lo := lo;
    PubDatas[i].FileSize.Hi := Hi;
    if not _Manager._Controller._HashManager.GetHash(fn, PubDatas[i].FileHash) then
    begin
      PubDatas[i].FileHash := _Manager._Controller._HashManager.CalcNewHash(fn);
      _Manager._Controller._HashManager.Save;
    end;
  end;
end;

procedure TPKSTPublishTask.ProcReply(var Data; Size: Integer);
begin
  inherited;
  PublishNext;
end;

procedure TPKSTPublishTask.Publish;
var
  PKSTHead: TPKSTHead;
  Guid: TGUID;
  i: Integer;
  M: TMemoryStream;
begin
  CreateGUID(Guid);
  TaskID := GUID;
  PKSTHead.Flag := PKSTFLAG;
  PKSTHead.Version := PKSTVERSION;
  PKSTHead.DataType := PKST_PUBLISH;
  M := TMemoryStream.Create;
  M.Write(PKSTHead, Sizeof(TPKSTHead));
  M.Write(Guid, Sizeof(TGUID));
  M.Write(PubDatas[CurData].FileSize, Sizeof(Int64rec));
  M.Write(PubDatas[CurData].FileHash, Sizeof(TMD5Digest));
  M.Write(PubDatas[CurData].FileName[1], Length(PubDatas[CurData].FileName));
  PKSTHead.DataSize := M.Size-Sizeof(TPKSTHead);
  m.Seek(0,0);
  M.Write(PKSTHead, Sizeof(TPKSTHead));
  m.Seek(0,0);
  setlength(SendData, M.Size);
  M.Read(SendData[0],M.Size);
  SendSize := M.size;
  M.Free;
  Host := GStack.TInAddrToString(PubNodes[CurNode].IP);
  Port := PubNodes[CurNode].Port;
  DoSend;
end;

procedure TPKSTPublishTask.PublishNext;
begin
  if CurNode<High(PubNodes) then
  begin
    CurNode := CurNode + 1;
    if Assigned(FonPublish) then
      FonPublish(self);
    Publish;
  end
  else
  begin
    CurNode := 0;
    if CurData<High(PubDatas) then
      CurData := CurData + 1
    else
    begin
      if Assigned(FonEndPublish) then
        FonEndPublish(self);
      Terminate;
      exit;
    end;
    if Assigned(FonPublish) then
      FonPublish(self);
    Publish;
  end;
end;

{ TPKSTDownloadTask }

constructor TPKSTDownloadTask.Create(Manager: TPKSTTaskManager);
begin
  inherited;
  SendSize := 0;
end;

procedure TPKSTDownloadTask.DoTimeOut;
begin
  inherited;
  if Node.LocalIP.S_addr= GStack.StringToTInAddr(Host).S_addr then
  begin
    Host := GStack.TInAddrToString(Node.IP);
    Port := Node.Port;
    DoSend;
  end
  else
    Terminate;
end;

procedure TPKSTDownloadTask.ProcReply(var Data; Size: Integer);
begin
  inherited;
  Terminate;
end;

{ TPKSTDownLoadManager }

procedure TPKSTDownLoadManager.AddDownload(
  Downloadinfo: TPKSTDownLoadInfo);
var
  DLTaskInfo : TPKSTDLTaskInfo;
begin
//  EnterCriticalSection(SECTION);
  DLTaskInfo := TPKSTDLTaskInfo.Create;
  DLTaskInfo.DownloadInfo := Downloadinfo;
  _WaitQueue.Push(DLTaskInfo);

  if Assigned(FOnDownloadNotify) then
    FOnDownloadNotify(self);
//  LeaveCriticalSection(SECTION);
end;

procedure TPKSTDownLoadManager.AddDownload(FileName: string;
  Filehash: TMD5Digest; FileSize: Int64; host: string; port: Integer);
var
  thread: TPKSTADDDLThread;
begin
  thread := TPKSTADDDLThread.Create(self);
  thread.FileName := FileName;
  thread.FileHash := Filehash;
  thread.FileSize := FileSize;
  thread.Host := host;
  thread.Port := port;
  thread.Resume;
end;

procedure TPKSTDownLoadManager.AddThread(Thread: TPKSTADDDLThread);
begin
  _ThreadList.Add(Thread);
end;

procedure TPKSTDownLoadManager.CompleteDownload(DownloadID: TGUID);
var
  i: Integer;
  TmpFileName: string;
  DLTaskInfo : TPKSTDLTaskInfo;
  NodeInfoEx : TPKSTNodeInfoEX;
  HashData: TPKSTHashData;
  HFile: THandle;
begin
  for i := _DownloadQueue.Count-1  downto 0 do
  begin
    DLTaskInfo := TPKSTDLTaskInfo(_DownloadQueue.List[i]);
    if IsEqualGUID(DLTaskInfo.DownloadID, DownloadID) then
    begin
      FileClose(DLTaskInfo.HFile);
      FileClose(DLTaskInfo.HifoFile);
      TmpFileName := SlashSep(ExtractFilePath(Application.ExeName),'Tmp\'+ExtractFileName(DLTaskInfo.DownloadInfo.SaveFileName+'.tmp'));
      if FileExists(DLTaskInfo.DownloadInfo.SaveFileName) then
        DeleteFile(DLTaskInfo.DownloadInfo.SaveFileName);
      MoveFile(Pchar(TmpFileName), pchar(DLTaskInfo.DownloadInfo.SaveFileName));
      DeleteFile(TmpFileName);
      DeleteFile(TmpFileName+'.ifo');
      HashData.FileName := DLTaskInfo.DownloadInfo.SaveFileName;
      HashData.FileSize := DLTaskInfo.DownloadInfo.FileSize;
      HashData.FileHash := DLTaskInfo.DownloadInfo.FileHash;
      HFile := FileOpen(DLTaskInfo.DownloadInfo.SaveFileName, fmOpenRead or fmShareDenyNone);
      if HFile<>INVALID_HANDLE_VALUE then
      begin
        GetFileTime(HFile, nil, nil, @HashData.FileTime);
        FileClose(HFile);
      end;
      _Controller._HashManager.CheckInHash(HashData);

      _CompliteQueue.Push(_DownloadQueue.list[i]);
      _DownloadQueue.list.Delete(i);
      if assigned(_Controller.fileproc) then
      begin
        NodeInfoEx := _Controller._NodeManager.GetActiveServerByHost(DLTaskInfo.DownloadHost, DLTaskInfo.DownloadPort);
        if NodeInfoEx = nil then
          NodeInfoEx := _Controller._NodeManager.GetDeActiveServerByHost(DLTaskInfo.DownloadHost, DLTaskInfo.DownloadPort);
        if NodeInfoEx <> nil then
        begin
          _Controller.fileproc(GUIDToString(NodeInfoEX.ServerID), DLTaskInfo.DownloadInfo.SaveFileName);
        end
        else
          _Controller.fileproc(DLTaskInfo.DownloadHost+':'+inttostr(DLTaskInfo.DownloadPort), DLTaskInfo.DownloadInfo.SaveFileName);
      end;
      if Assigned(FOnDownloadNotify) then
        FOnDownloadNotify(self); {}
      if Pos('SCH://', DLTaskInfo.DownloadInfo.FileName)=1 then
      begin
        if Assigned(_Controller.FOnScheduleChange) then
          _Controller.FOnScheduleChange(self); {}
      end;
      Exit;
    end;
  end;
end;

constructor TPKSTDownLoadManager.Create(Controller: TPKSTController);
begin
  _Controller := Controller;
  _ThreadList := TList.Create;

  _DownloadQueue := TPKSTQueue.Create;   // 下载队列
  _WaitQueue := TPKSTQueue.Create;       // 等待队列
  _CompliteQueue := TPKSTQueue.Create;   // 完成队列

//  _DownLoadCount := 0;
//  setlength(_DownLoadPool,32);
//  _WaitCount := 0;
//  setlength(_WaitPool,32);
  _Timer := TTimer.Create(nil);
  _Timer.Interval := 1000;
  _Timer.OnTimer := DoTimeer;
//  InitializeCriticalSection(SECTION);
end;

procedure TPKSTDownLoadManager.DeleteThread(Thread: TPKSTADDDLThread);
var
  i:  Integer;
begin
  i := _ThreadList.IndexOf(Thread);
  if i>=0 then
    _ThreadList.Delete(i);
end;

destructor TPKSTDownLoadManager.Destroy;
var
  i: integer;
begin
//  DeleteCriticalSection(SECTION);
  _Timer.Enabled := false;
  _Timer.Free;

  for i:= _DownloadQueue.List.Count-1 downto 0 do
    TPKSTDLTaskInfo(_DownloadQueue.List[i]).Free;

  for i:= _WaitQueue.List.Count-1 downto 0 do
    TPKSTDLTaskInfo(_WaitQueue.List[i]).Free;

  for i:= _CompliteQueue.List.Count-1 downto 0 do
    TPKSTDLTaskInfo(_CompliteQueue.List[i]).Free;

  for i:= _ThreadList.Count-1 downto 0 do
    TPKSTADDDLThread(_ThreadList.Items[i]).Terminate;
  _ThreadList.Free;
  inherited;
end;

procedure TPKSTDownLoadManager.DoTimeer(Sender: Tobject);
var
  DownloadInfo: TPKSTDLTaskInfo;
  DisconnectHost: string;
  Disconnectport: Integer;
  DisconnectServer: string;
  i, j: Integer;
  NodeInfo: TPKSTNodeInfo;
  NodeInfoEX: TPKSTNodeInfoEX;
  ip: TIdInAddr;
begin
  try
    for i:= _DownloadQueue.Count-1 downto 0 do
    begin
      DownloadInfo := TPKSTDLTaskInfo(_DownloadQueue.List[i]);
      if DownloadInfo.Lastresponse=DownloadInfo.StartTime then
      begin
        if Now-DownloadInfo.Lastresponse > (PKST_DATA_TIMEOUT)/86400 then
        begin
          DisconnectHost := DownloadInfo.DownloadHost;
          Disconnectport := DownloadInfo.DownloadPort;
          DisconnectServer := DownloadInfo.DownloadInfo.PublishServer;
          FailDownload(DownloadInfo.DownloadID);
          if _Controller._NodeManager.CheckActiveNode(DisconnectHost, Disconnectport) then
            Continue;
          NodeInfoEX := _Controller._NodeManager.GetActiveServerByHost(DisconnectServer);
          if NodeInfoEX = nil then
            NodeInfoEX := _Controller._NodeManager.GetDeActiveServerByHost(DisconnectServer);
          if NodeInfoEX = nil then
            Continue;

          Ip := GStack.StringToTInAddr(DisconnectHost);
          for j := 0 to NodeInfoEX.Nodes.Count-1 do
          begin
            NodeInfo := TPKSTNodeInfo(NodeInfoEX.Nodes.Items[j]);
            if ((NodeInfo.NodeAdd.IP.S_addr = ip.S_addr) and (Disconnectport=NodeInfo.NodeAdd.Port)) then
            begin
              NodeInfoEX.Nodes.Extract(NodeInfo).Free;
              Break;
            end;
          end;
        end;
      end
      else
        if Now-DownloadInfo.Lastresponse > PKST_DATA_TIMEOUT/86400 then
        begin
          FailDownload(DownloadInfo.DownloadID);
        end;
    end;

    while (_DownloadQueue.Count<_Controller._MaxDownLoads) and (_WaitQueue.Count>0) do
      StartDownload;
  finally
  end;
end;

procedure TPKSTDownLoadManager.FailDownload(DownloadID: TGUID);
var
  i: Integer;
  Downloadinfo: TPKSTDLTaskInfo;
  function Downloadfromother: Boolean;
  var
    Guid: TGuid;
    j,Idx: Integer;
    IP: TIdInAddr;
    NodeInfo: TPKSTNodeInfo;
    NodeInfoEx: TPKSTNodeInfoEX;
  begin
    CreateGUID(Guid);
    Downloadinfo.DownloadID := GUID;
    Downloadinfo.StartTime := Now;
    Downloadinfo.Lastresponse := Downloadinfo.StartTime;
    Downloadinfo.TryCount := 0;
    Result := false;
    NodeInfoEx := _Controller._NodeManager.GetActiveServerByHost(Downloadinfo.DownloadInfo.PublishServer);
    if NodeInfoEx<>nil then
    begin
      if NodeInfoEx.Nodes.Count <= 0 then
      begin
        _Controller.ASKNodeList(Downloadinfo.DownloadInfo.PublishServer, PKST_SERVERPORT, Downloadinfo.DownloadInfo.PublishServer);
        Exit;
      end;
      Idx := 0;
      if not _Controller._NodeManager.CheckActiveNode(Downloadinfo.DownloadHost, Downloadinfo.DownloadPort) then
        for j := 0 to NodeInfoEx.Nodes.Count-1 do
        begin
          NodeInfo := TPKSTNodeInfo(NodeInfoEx.Nodes.Items[j]);
          Ip := GStack.StringToTInAddr(Downloadinfo.DownloadHost);
          if ((NodeInfo.NodeAdd.IP.S_addr = Ip.S_addr) and (NodeInfo.NodeAdd.Port = Downloadinfo.DownloadPort)) or
             ((NodeInfo.NodeAdd.LocalIP.S_addr = Ip.S_addr) and (Downloadinfo.DownloadPort = PKST_Clientport))
          then
          begin
            Idx := J+1;
            break;
          end;
        end;
    end
    else
    begin
      NodeInfoEx := _Controller._NodeManager.GetDeActiveServerByHost(Downloadinfo.Downloadinfo.PublishServer);
      if (NodeInfoEx=nil) or (NodeInfoEx.Nodes.Count <=0) then
        Exit;
      if NodeInfoEx.Nodes.Count < 2 then
      begin
        Idx := 0;
        NodeInfo := TPKSTNodeInfo(NodeInfoEx.Nodes.Items[0]);
        if NodeInfo.NodeAdd.LocalIP.S_addr<>0 then
        begin
          Downloadinfo.DownloadHost := GStack.TInAddrToString(NodeInfo.NodeAdd.LocalIP);
          Downloadinfo.DownloadPort := PKST_CLIENTPORT;
        end
        else
        begin
          Downloadinfo.DownloadHost := GStack.TInAddrToString(NodeInfo.NodeAdd.IP);
          Downloadinfo.DownloadPort := NodeInfo.NodeAdd.Port;
        end;
        _Controller.ASKNodeList(Downloadinfo.DownloadHost, Downloadinfo.DownloadPort, Downloadinfo.DownloadInfo.PublishServer);
      end
      else
        for j := 0 to NodeInfoEx.Nodes.Count-1 do
        begin
          NodeInfo := TPKSTNodeInfo(NodeInfoEx.Nodes.Items[j]);
          Ip := GStack.StringToTInAddr(Downloadinfo.DownloadHost);
          if ((NodeInfo.NodeAdd.IP.S_addr = Ip.S_addr) and (NodeInfo.NodeAdd.Port = Downloadinfo.DownloadPort)) or
             ((NodeInfo.NodeAdd.LocalIP.S_addr = Ip.S_addr) and (Downloadinfo.DownloadPort = PKST_Clientport))
          then
          begin
            Idx := J+1;
            break;
          end;
        end;
    end;

    if idx>= NodeInfoEx.Nodes.Count then
      exit;
    NodeInfo := TPKSTNodeInfo(NodeInfoEx.Nodes.Items[idx]);

    if NodeInfo.NodeAdd.LocalIP.S_addr<>0 then
    begin
      Downloadinfo.DownloadHost := GStack.TInAddrToString(NodeInfo.NodeAdd.LocalIP);
      Downloadinfo.DownloadPort := PKST_CLIENTPORT;
    end
    else
    begin
      Downloadinfo.DownloadHost := GStack.TInAddrToString(NodeInfo.NodeAdd.IP);
      Downloadinfo.DownloadPort := NodeInfo.NodeAdd.Port;
    end;
    Downloadinfo.Lastresponse := now;
    _Controller.Download(NodeInfo.NodeAdd,
      DownloadInfo.DownloadID,DownloadInfo.DownloadInfo.FileHash,
      DownloadInfo.DownloadInfo.FileSize,DownloadInfo.DownloadInfo.FileName,
      DownloadInfo.DownloadInfo.DownloadedSize);
    Result := true;
  end;
begin
  for i := _DownloadQueue.Count-1 downto 0 do
    if IsEqualGUID(TPKSTDLTaskInfo(_DownloadQueue.List[i]).DownloadID, DownloadID) then
    begin
      Downloadinfo := TPKSTDLTaskInfo(_DownloadQueue.List[i]);
      if Downloadfromother then
      begin
        if Assigned(FOnDownloadNotify) then
          FOnDownloadNotify(self);
        exit;
      end;

      if Downloadinfo.HFile<>0 then
        FileClose(Downloadinfo.HFile);
      if Downloadinfo.HifoFile<>0 then
        FileClose(Downloadinfo.HifoFile);
      Downloadinfo.HFile := 0;
      Downloadinfo.HifoFile := 0;
      _WaitQueue.Push(Downloadinfo);
      _DownloadQueue.List.Delete(i);

      if Assigned(FOnDownloadNotify) then
        FOnDownloadNotify(self);
    end;
end;

function TPKSTDownLoadManager.GetCompliteCount: Integer;
begin
  Result := _CompliteQueue.Count;
end;

function TPKSTDownLoadManager.GetCompliteInfo(
  Idx: Integer): TPKSTDLTaskInfo;
begin
  if (idx<0) or (idx>=_CompliteQueue.Count) then exit;
  Result := TPKSTDLTaskInfo(_CompliteQueue.list[idx]);
end;

function TPKSTDownLoadManager.GetDownLoadCount: Integer;
begin
  Result := _DownloadQueue.Count;
end;

function TPKSTDownLoadManager.GetDownloadInfo(
  Idx: Integer): TPKSTDLTaskInfo;
begin
  if (idx<0) or (idx>=_DownloadQueue.Count) then exit;
  Result := TPKSTDLTaskInfo(_DownloadQueue.list[idx]);
end;

function TPKSTDownLoadManager.GetWaitCount: Integer;
begin
  Result := _WaitQueue.Count;
end;

function TPKSTDownLoadManager.GetWaiteInfo(
  Idx: Integer): TPKSTDLTaskInfo;
begin
  if (idx<0) or (idx>=_WaitQueue.Count) then exit;
  Result := TPKSTDLTaskInfo(_WaitQueue.list[idx]);
end;
{}
procedure TPKSTDownLoadManager.ProceDownloadData(AData: TStream;
  ABinding: TIdSocketHandle);
var
  PKSTHead   : TPKSTHead;
  DataID     : TGUID;
  TaskID     : TGUID;
  FileSize   : Int64;
  StartPos   : Int64;
  DataSize   : word;
  Crc32      : DWord;
  FileData   : array of Byte;
  i          : Integer;
  idx        : Integer;
  FileName   : string;
  TmpFileName: string;
  hFile      : THandle;
  state      : Byte;
  s          : Tstrings;
  DLTaskInfo : TPKSTDLTaskInfo;
  Found      : Boolean;
  hi         : Cardinal;
{$IFDEF PKSTDEBUG}
  f          : TextFile;
{$ENDIF}
begin
  AData.Read(PKSTHead, Sizeof(TPKSTHead));
  AData.Read(DataID, Sizeof(TGUID));
  AData.Read(TaskID, Sizeof(TGUID));
  AData.Read(FileSize, Sizeof(Int64));
  AData.Read(StartPos, Sizeof(Int64));
  AData.Read(DataSize, Sizeof(word));
  AData.Read(Crc32, Sizeof(Dword));
  Setlength(FileData, DataSize);
  AData.Read(FileData[0], DataSize);

{$IFDEF PKSTDEBUG}
  AssignFile(f, 'C:\PKSTLOG.Txt');
  if not FileExists('C:\PKSTLOG.Txt') then
    Rewrite(f)
  else
    Append(f);
  Writeln(f, GUIDToString(DataID)+' '+Inttostr(StartPos));
  CloseFile(f);
{$ENDIF}

  idx := 0;
  try
    Found := false;
    for i := _DownloadQueue.Count-1  downto 0 do
    begin
      DLTaskInfo := TPKSTDLTaskInfo(_DownloadQueue.List[i]);
      if IsEqualGUID(DLTaskInfo.DownloadID, TaskID) then
      begin
        DLTaskInfo.Lastresponse := Now;
        idx := i;
        Found := True;
        break;
      end;
    end;

    if not Found then
    begin
      FailDownload(TaskID);
      ReplyState(ABinding, DataID, PKST_DATA_ERR, DLTaskInfo.DownloadInfo.DownloadedSize);
      exit;
    end;

    if StartPos <> DLTaskInfo.DownloadInfo.DownloadedSize then
    begin
      ReplyState(DLTaskInfo, DataID, PKST_DATA_TRY, DLTaskInfo.DownloadInfo.DownloadedSize);
      exit;
    end;


    if _Controller._CRCCheck then
      if Crc32_P(@Filedata[0], DataSize)<>Crc32 then
      begin
        DLTaskInfo.TryCount := DLTaskInfo.TryCount+1;
        if DLTaskInfo.TryCount < 3 then
          state := PKST_DATA_TRY
        else
        begin
          state := PKST_DATA_ERR;
          FailDownload(TaskID);
        end;
        ReplyState(DLTaskInfo, DataID, state, DLTaskInfo.DownloadInfo.DownloadedSize);
        exit;
      end;
{}
    DLTaskInfo.TryCount := 0;

    if DLTaskInfo.HFile<>0 then
    try
      hi := int64Rec(StartPos).Hi;
      SetFilePointer(DLTaskInfo.HFile, int64Rec(StartPos).Lo, @Hi, FILE_BEGIN);
      FileWrite(DLTaskInfo.HFile, Filedata[0], DataSize);
//      if StartPos+DataSize > DLTaskInfo.DownloadInfo.DownloadedSize then
      DLTaskInfo.DownloadInfo.DownloadedSize := StartPos+DataSize;

//      if DLTaskInfo.DownloadInfo.DownloadedSize mod 16384 =0 then
      begin
        s := TStringList.Create;
        s.Add(DLTaskInfo.DownloadInfo.FileName);
        s.Add(DLTaskInfo.DownloadInfo.PublishServer);
        s.Add(MD5DigestToStr(DLTaskInfo.DownloadInfo.FileHash));
        s.Add(IntToStr(DLTaskInfo.DownloadInfo.FileSize));
        s.Add(IntToStr(DLTaskInfo.DownloadInfo.DownloadedSize));
        FileSeek(DLTaskInfo.HifoFile, 0, FILE_BEGIN);
        SetEndOfFile(DLTaskInfo.HifoFile);
        FileWrite(DLTaskInfo.HifoFile, s.text[1], length(s.text));
//        s.SaveToFile(DLTaskInfo.TempFileName+'.ifo');
        s.Free;
      end;
{}
      if DLTaskInfo.DownloadInfo.DownloadedSize >= DLTaskInfo.DownloadInfo.FileSize then
      begin
        CompleteDownload(TaskID);
      end;

      state := PKST_DATA_OK;
      ReplyState(DLTaskInfo, DataID, state, DLTaskInfo.DownloadInfo.DownloadedSize);
      if Assigned(_Controller.rcvproc) then
        _Controller.rcvproc(DLTaskInfo.DownloadInfo.SaveFileName,DLTaskInfo.DownloadInfo.FileSize,DLTaskInfo.DownloadInfo.DownloadedSize);
      if Assigned(FOnDownloadNotify) then
        FOnDownloadNotify(self);

      exit;
    except

    end;

    state := PKST_DATA_ERR;
    FailDownload(TaskID);
    ReplyState(DLTaskInfo, DataID, state, DLTaskInfo.DownloadInfo.DownloadedSize);
  finally
  end;
end;

procedure TPKSTDownLoadManager.ReplyState(DLTaskInfo: TPKSTDLTaskInfo; GUID: TGUID; State: Byte; StartPos: Int64);
var
  Data       : array of Char;
  M          : TMemoryStream;
begin
  M := TMemoryStream.Create;
  M.Write(GUID, Sizeof(TGUID));
  M.Write(state, Sizeof(Byte));
  M.Write(StartPos, Sizeof(Int64));
  M.Seek(0,0);
  setlength(Data, M.size);
  M.Read(Data[0],M.size);
  M.free;
  _Controller.REPLY(DLTaskInfo.DownloadHost, DLTaskInfo.DownloadPort, Data[0], Length(Data));
end;

procedure TPKSTDownLoadManager.ReplyState(ABinding: TIdSocketHandle;
  GUID: TGUID; State: Byte; StartPos: Int64);
var
  Data       : array of Char;
  M          : TMemoryStream;
begin
  M := TMemoryStream.Create;
  M.Write(GUID, Sizeof(TGUID));
  M.Write(state, Sizeof(Byte));
  M.Write(StartPos, Sizeof(Int64));
  M.Seek(0,0);
  setlength(Data, M.size);
  M.Read(Data[0],M.size);
  M.free;
  _Controller.REPLY(ABinding.PeerIP, ABinding.PeerPort, Data[0], Length(Data));
end;

procedure TPKSTDownLoadManager.StartDownload;
var
  GUID: TGUID;
  DLTaskInfo : TPKSTDLTaskInfo;
  FileName   : string;
  TmpFileName: string;
  hFile: THandle;
begin
//  Result := false;
  if _DownloadQueue.Count >=_Controller._MaxDownloads then exit;
  if _WaitQueue.Count <= 0 then exit;
  CreateGUID(GUID);

  DLTaskInfo := TPKSTDLTaskInfo(_WaitQueue.Peek);

  FileName := DLTaskInfo.DownloadInfo.SaveFileName;
  TmpFileName := SlashSep(ExtractFilePath(Application.ExeName),'Tmp\'+ExtractFileName(FileName)+'.tmp');
  DLTaskInfo.TempFileName := TmpFileName;

  if not FileExists(TmpFileName) then
  begin
    DLTaskInfo.hFile := FileCreate(TmpFileName);
    DLTaskInfo.HifoFile := FileCreate(TmpFileName+'.ifo');
  end
  else
  begin
    DLTaskInfo.hFile := FileOpen(TmpFileName, fmOpenReadWrite  or fmShareDenyWrite);
    DLTaskInfo.HifoFile := FileOpen(TmpFileName+'.ifo', fmOpenReadWrite  or fmShareDenyWrite);
  end;
  if (DLTaskInfo.hFile=INVALID_HANDLE_VALUE) or (DLTaskInfo.HifoFile=INVALID_HANDLE_VALUE) then
  begin
    DLTaskInfo.HFile := 0;
    DLTaskInfo.HifoFile := 0;
//    Exit;
  end;

  DLTaskInfo := TPKSTDLTaskInfo(_WaitQueue.Pop);
  DLTaskInfo.DownloadID := GUID;
  DLTaskInfo.StartTime := Now;
  DLTaskInfo.Lastresponse := DLTaskInfo.StartTime;
  DLTaskInfo.TryCount := 0;
  _DownloadQueue.Push(DLTaskInfo);

  DLTaskInfo.DownloadHost := DLTaskInfo.DownloadInfo.PublishServer;
  DLTaskInfo.DownloadPort := PKST_SERVERPORT;
  DLTaskInfo.Node.LocalIP.S_addr := 0;

//  Result := true;
  if Assigned(FOnDownloadNotify) then
    FOnDownloadNotify(self);

  _Controller.Download(DLTaskInfo.DownloadHost, DLTaskInfo.DownloadPort,
      DLTaskInfo.DownloadID, DLTaskInfo.DownloadInfo.FileHash,
      DLTaskInfo.DownloadInfo.FileSize, DLTaskInfo.DownloadInfo.FileName,
      DLTaskInfo.DownloadInfo.DownloadedSize);
end;

{ TPKSTDataTask }

function TPKSTDataTask.Checktask(CheckID: TGUID): Boolean;
var
  i: Integer;
begin
  result := False;
  for i := _DataWindowPos-1 downto 0 do
    if IsEqualGUID(CheckID, _DataWindow[i].TaskID) then
    begin
      Result := True;
    end;
end;

constructor TPKSTDataTask.Create(TaskManager : TPKSTTaskManager; UpLoadManager : TPKSTUpLoadManager);
begin
  inherited Create(TaskManager);
  _UpLoadManaget := UpLoadManager;
  _UpLoadManaget.AddTask(Self);
  RetryCount := 0;
  _File := INVALID_HANDLE_VALUE;
  _DataWindowPos := 0;
  _PackageLevel := 0;
  Application.Tag := 0;
  _WINDOWSize := 2;
  _RetryFlag := False;
  _Trying := True;
  _TryingCount := 0;
end;

destructor TPKSTDataTask.Destroy;
begin
  try
    if _File <> INVALID_HANDLE_VALUE then
      FileClose(_File);
  except
  end;
  _UpLoadManaget.DeleteTask(Self);
  inherited;
end;

procedure TPKSTDataTask.DoSend;
begin
  if SendSize>0 then
    _Manager._Controller._UDPController.Send(Host, Port, SendData[0], SendSize);
end;

procedure TPKSTDataTask.DoTimeOut;
var
  limit: Integer;
  OldPackageLevel : Integer;
begin
  inherited;
  _TryingCount := 0;
  RetryCount := RetryCount+1;
  limit := 3;
  case _PackageLevel of
    0..7: limit := 3;
    8..11: limit := 2;
    12..15: limit := 1;
  end;
  if RetryCount<limit then
  begin
    StartPos := _DataWindow[0].StartPos;
    _DataWindowPos := 0;
    if _RetryFlag then
    begin
      OldPackageLevel := _PackageLevel;
      _PackageLevel := 0;
      send;
      _PackageLevel := OldPackageLevel;
    end
    else
      send;
  end
  else
    if _PackageLevel>0 then
    begin
      _Trying := False;
      _PackageLevel := _PackageLevel-1;
      case _PackageLevel of
        0..3: _WINDOWSize := 2;
        4..7: _WINDOWSize := 1;
        8..15: _WINDOWSize := 1;
      end;
      RetryCount := 0;
      StartPos := _DataWindow[0].StartPos;
      _DataWindowPos := 0;
      OldPackageLevel := _PackageLevel;
      Application.Tag := _PackageLevel;
      _PackageLevel := 0;
      _RetryFlag := True;
      send;
      _PackageLevel := OldPackageLevel;
//      Application.Title := inttostr(PKST_DATASIZES[_PackageLevel]);
    end
    else
      Terminate;
end;

procedure TPKSTDataTask.Execute;
begin
  try
    _File := FileOpen(RealFileName, fmOpenRead or fmShareDenyWrite);
    FileClose(_File);
    _File := INVALID_HANDLE_VALUE;
  except
    Terminate;
    exit;
  end;
  Synchronize(Send);
  while not Terminated do
  begin
    if Now-LastResponseTime > TimeOut then
      DoTimeOut;
    Sleep(550);
  end;
end;

procedure TPKSTDataTask.ProcReply(var Data; Size: Integer);
var
  state: Byte;
  M: TMemoryStream;
  i: integer;
  RStartPos: Int64;
  upcount: Integer;
{$IFDEF PKSTDEBUG}
  f: TextFile;
{$ENDIF}
begin
  inherited;
  if size<(Sizeof(TGUID)+1) then
  begin
    RetryCount := RetryCount+1;
    if RetryCount<2 then
      send;
  end
  else
  begin
{$IFDEF PKSTDEBUG}
  AssignFile(f, 'C:\PKSTLOG.Txt');
  if not FileExists('C:\PKSTLOG.Txt') then
    Rewrite(f)
  else
    Append(f);
  Writeln(f, GUIDToString(TaskID)+' '+Inttostr(StartPos));
  CloseFile(f);
{$ENDIF}

  M := TMemoryStream.Create;
  M.Write(Data,Size);
  M.Seek(Sizeof(TGUID),0);
  M.Read(State, Sizeof(Byte));
  M.Read(RStartPos, Sizeof(Int64));
  M.Free;
  Case State of
    PKST_DATA_OK :
    begin
      if _RetryFlag then
        _RetryFlag := False
      else
      begin
        if _Trying then
          upcount := 2+_WINDOWSize
        else
          upcount := 48 + (1024 shr ((_PackageLevel+1) div 2));
        RetryCount := 0;
        if (_PackageLevel<15) then
        begin
          _TryingCount := _TryingCount+1;
          if _TryingCount>upcount then
          begin
            _PackageLevel := _PackageLevel+1;
            _TryingCount := 0;
            if _PackageLevel>=15 then
              _Trying := False;
          end;
        end;
      end;
      for i := _DataWindowPos-1 downto 1 do
        _DataWindow[i-1] := _DataWindow[i];
      _DataWindowPos := _DataWindowPos-1;
      case _PackageLevel of
        0..3: _WINDOWSize := 2;
        4..7: _WINDOWSize := 1;
        8..15: _WINDOWSize := 1;
      end;
      SendNext;
    end;
    PKST_DATA_ERR : Terminate;
    PKST_DATA_TRY :
      begin
        _TryingCount := 0;
        RetryCount := RetryCount+1;
        if RetryCount<2 then
        begin
          StartPos := RStartPos;
          _DataWindowPos := 0;
          send;
//          resend;
        end
        else
          Terminate;
      end;
    end;
  end;
end;

procedure TPKSTDataTask.resend;
var
  i: Integer;
begin
  LastResponseTime := Now;
  for i:= 0 to _DataWindowPos-1 do
    if _DataWindow[i].SendDataSize>0 then
      _Manager._Controller._UDPController.Send(Host, Port, _DataWindow[i].SendData[0], _DataWindow[i].SendDataSize);
end;

procedure TPKSTDataTask.Send;
var
  PKSTHead: TPKSTHead;
  Buf: array of Byte;
  wSzie: Word;
  Crc: DWord;
  Guid: TGUID;
  i: Integer;
  M: TMemoryStream;
  hi: Cardinal;
begin
  if (StartPos>=FileSize) and (_DataWindowPos=0) then
    Terminate;
  LastResponseTime := Now;
  setlength(Buf, PKST_DATASIZES[_PackageLevel]);
  CreateGUID(Guid);
  TaskID := GUID;
  PKSTHead.Flag := PKSTFLAG;
  PKSTHead.Version := PKSTVERSION;
  PKSTHead.DataType := PKST_DATA;

  try
    if _File = INVALID_HANDLE_VALUE then
      _File := FileOpen(RealFileName, fmOpenRead or fmShareDenyWrite);
    hi := int64Rec(StartPos).Hi;
    SetFilePointer(_File, int64Rec(StartPos).Lo, @Hi, FILE_BEGIN);
    ReadFile(_File, Buf[0], PKST_DATASIZES[_PackageLevel], ReadSize, nil);
  except
    Terminate;
    exit;
  end;
  if _Manager._Controller._CRCCheck then
    Crc := Crc32_P(@Buf[0], ReadSize)
  else
    Crc := 0;

  M := TMemoryStream.Create;
  M.Write(PKSTHead, Sizeof(TPKSTHead));
  M.Write(Guid, Sizeof(TGUID));
  M.Write(DownloadTaskID, Sizeof(TGUID));
  M.Write(FileSize, Sizeof(Int64));
  M.Write(StartPos, Sizeof(Int64));
  wSzie := ReadSize;
  M.Write(wSzie, Sizeof(Word));
  M.Write(Crc, Sizeof(DWord));
  M.Write(Buf[0], wSzie);

  PKSTHead.DataSize := M.Size-Sizeof(TPKSTHead);
  m.Seek(0,0);
  M.Write(PKSTHead, Sizeof(TPKSTHead));
  m.Seek(0,0);
  setlength(SendData, M.Size);
  M.Read(SendData[0],M.Size);
  SendSize := M.size;
  M.Free;
  DoSend;
  _DataWindow[_DataWindowPos].TaskID := GUID;
  _DataWindow[_DataWindowPos].StartPos := StartPos;
  _DataWindow[_DataWindowPos].SendDataSize := SendSize;
  setlength(_DataWindow[_DataWindowPos].SendData, SendSize);
  Move(SendData[0], _DataWindow[_DataWindowPos].SendData[0], SendSize);
  _DataWindowPos := _DataWindowPos+1;
//  sendnext;
end;

procedure TPKSTDataTask.SendNext;
var
  Node: TPKSTNodeInfo;
begin
  if (StartPos>=FileSize) and (_DataWindowPos=0) then
    Terminate;
  while (StartPos<FileSize) and (_DataWindowPos<=_WINDOWSize-1) do
  begin
    StartPos := StartPos + ReadSize;
//    Inc(_DataWindowPos);
    Send;
  end;
  if assigned(_Manager._Controller.sendproc) then
  begin
    node := _Manager._Controller._NodeManager.GetActiveNodeByHost(Host,Port);
    if node=nil then
      node := _Manager._Controller._NodeManager.GetDeActiveNodeByHost(Host,Port);
    if Node<>nil then
      _Manager._Controller.sendproc(GUIDToString(node.NodeID), RealFileName, FileSize, StartPos);
  end;

  if assigned(_Manager._Controller.FonUpLoadNotify) then
    _Manager._Controller.FonUpLoadNotify(_UpLoadManaget);
//  else
//
end;

{ TPKSTADDDLThread }

procedure TPKSTADDDLThread.AddDownload;
begin
  _Manager.AddDownload(DownloadInfo);
end;

constructor TPKSTADDDLThread.Create(Manager: TPKSTDownLoadManager);
begin
  inherited Create(True);
  FreeOnTerminate := True;
  _Manager := Manager;
  Manager.addThread(Self);
end;

destructor TPKSTADDDLThread.Destroy;
begin
  _Manager.DeleteThread(self);
  inherited;
end;

procedure TPKSTADDDLThread.Execute;
var
  hash: TMD5Digest;
  path: string;
  SaveFileName: string;
  SaveFileNameExt: string;
  i: Integer;
  FileExt: string;
  Lo,Hi: Cardinal;
  HFile: THandle;
  addstr: string;
  addcount: Integer;
  pnf: textfile;
  stmp: string;
  DLTaskInfo: TPKSTDLTaskInfo;
  NodeInfoEx: TPKSTNodeInfoEX;
  FileMD5: TMD5Digest;
begin
  DownloadInfo.PublishServer := '';

  NodeInfoEx := _Manager._Controller._NodeManager.GetActiveServerByHost(Host, Port);
  if NodeInfoEx<>nil then
    DownloadInfo.PublishServer := NodeInfoEx.ServerHost
  else
  begin
    NodeInfoEx := _Manager._Controller._NodeManager.GetDeActiveServerByHost(Host, Port);
    if NodeInfoEx<>nil then
      DownloadInfo.PublishServer := NodeInfoEx.ServerHost;
  end;

  if DownloadInfo.PublishServer = '' then exit;

  for i := _Manager._DownloadQueue.Count-1 downto 0 do
  begin
    DLTaskInfo := TPKSTDLTaskInfo(_Manager._DownloadQueue.List[i]);
    if (DLTaskInfo.DownloadInfo.FileName = FileName) and
       MD5DigestCompare(DLTaskInfo.DownloadInfo.FileHash, FileHash) and
       (DLTaskInfo.DownloadInfo.FileSize = FileSize) then
      exit;
  end;

  for i := _Manager._WaitQueue.Count-1 downto 0 do
  begin
    DLTaskInfo := TPKSTDLTaskInfo(_Manager._WaitQueue.List[i]);
    if (DLTaskInfo.DownloadInfo.FileName = FileName) and
       MD5DigestCompare(DLTaskInfo.DownloadInfo.FileHash, FileHash) and
       (DLTaskInfo.DownloadInfo.FileSize = FileSize) then
      exit;
  end;

  for i := _Manager._CompliteQueue.Count-1 downto 0 do
  begin
    DLTaskInfo := TPKSTDLTaskInfo(_Manager._CompliteQueue.List[i]);
    if (DLTaskInfo.DownloadInfo.FileName = FileName) and
       MD5DigestCompare(DLTaskInfo.DownloadInfo.FileHash, FileHash) and
       (DLTaskInfo.DownloadInfo.FileSize = FileSize) then
      exit;
  end;

  path := _Manager._Controller._RcvDir;
  DownloadInfo.FileName := FileName;
  DownloadInfo.FileHash := FileHash;
  DownloadInfo.FileSize := FileSize;
  if Pos('SCH://', FileName) <> 1 then
  begin
    SaveFileName := StringReplace(FileName,':','_',[rfReplaceAll]);
    SaveFileName := StringReplace(SaveFileName,'\','_',[rfReplaceAll]);
    SaveFileNameExt := ExtractFileExt(SaveFileName);
    SaveFileName := Copy(SaveFileName,1,length(SaveFileName)-length(SaveFileNameExt));
  end
  else
  begin
    SaveFileName := PKSTSCHFILENAME;
    path := ExtractFilePath(Application.ExeName);
  end;

  addstr := '';
  addcount := 1;
  hi := 0;

  while FileExists(SlashSep(path,SaveFileName+addstr+SaveFileNameExt)) do
  begin
    HFile := FileOpen(SlashSep(path,SaveFileName+addstr+SaveFileNameExt), fmOpenRead);
    if HFile<>INVALID_HANDLE_VALUE then
    begin
      lo := GetFileSize(HFile, @hi);
      if lo<>$FFFFFF then
      begin
        if (INt64Rec(FileSize).Lo=lo) and (INt64Rec(FileSize).Hi=Hi) then
        begin
          FileClose(HFile);
          HFile := 0;
          if not _Manager._Controller._HashManager.GetHash(SlashSep(path,SaveFileName+addstr+SaveFileNameExt), FileMD5) then
            break;
//            exit;
          if MD5DigestCompare(FileMD5, FileHash) then
            Exit;
        end;
      end
      else
        if GetLastError=NO_ERROR then
        begin
          if (INt64Rec(FileSize).Lo=lo) and (INt64Rec(FileSize).Hi=Hi) then
          begin
            FileClose(HFile);
            HFile := 0;
            if MD5DigestCompare(FileMD5, FileHash) then
              Exit;
          end;
        end;
      if HFile <> 0 then
        FileClose(HFile);
    end;
    break;
//    Inc(addcount);
//    addstr := '('+inttostr(addcount)+')';
  end;

  while FileExists(SlashSep(ExtractFilePath(Application.ExeName),'Tmp\'+SaveFileName+addstr+SaveFileNameExt+'.tmp.ifo')) do
  begin
    AssignFile(pnf, SlashSep(ExtractFilePath(Application.ExeName),'Tmp\'+SaveFileName+addstr+SaveFileNameExt+'.tmp.ifo'));
    try
      Reset(pnf);
    except
//      Inc(addcount);
//      addstr := '('+inttostr(addcount)+')';
//      Continue;
      break;
    end;
    try
      Readln(pnf, stmp);
      if stmp<>DownloadInfo.FileName then
        abort;
      Readln(pnf, stmp);
      if stmp<>DownloadInfo.PublishServer then
        abort;
      Readln(pnf, stmp);
      if not (MD5DigestToStr(DownloadInfo.FileHash)=stmp) then
        abort;
      Readln(pnf, stmp);
      if StrToInt64(stmp)<>DownloadInfo.FileSize then
        abort;
      Readln(pnf, stmp);
      DownloadInfo.DownloadedSize := StrToInt64(stmp);
      CloseFile(pnf);
      break;
    except
      CloseFile(pnf);
    end;
//    Inc(addcount);
//    addstr := '('+inttostr(addcount)+')';
    break;
  end;
  DownloadInfo.SaveFileName := SlashSep(path,SaveFileName+addstr+SaveFileNameExt);
  Synchronize(AddDownload);
end;

{ TPKSTADDUPLOADThread }

constructor TPKSTADDUPLOADThread.Create(Manager: TPKSTUpLoadManager);
begin
  inherited Create(True);
  FreeOnTerminate := True;
  _Manager := Manager;
  _Manager.addThread(Self);
  RealFileName := '';
end;

destructor TPKSTADDUPLOADThread.Destroy;
begin
  _Manager.DeleteThread(self);
  inherited;
end;

procedure TPKSTADDUPLOADThread.Execute;
var
  FName: string;
  FHash: TMD5Digest;
  FSize: Int64;
  Hi, Lo: Int64;
  HFile: THandle;
  FileMD5: TMD5Digest;
begin
  Hi := 0;
  Lo := 0;
  FName := FileName;

  if _Manager._Controller.ServerMode then
  begin
    if pos('SCH://',FileName) = 1 then
    FName := Copy(FileName, 7, length(FileName));
  end
  else
  begin
    if pos('SCH://',FileName) = 1 then
      FName := SlashSep(ExtractFilePath(Application.ExeName), PKSTSCHFILENAME)
    else
    begin
      FName := StringReplace(FileName,':','_',[rfReplaceAll]);
      FName := StringReplace(FName,'\','_',[rfReplaceAll]);
      FName := SlashSep(_Manager._Controller._RcvDir, FName);
    end;
  end;
  RealFileName := FName;

  if not FileExists(FName) then exit;
  HFile := FileOpen(FName, fmOpenRead or fmShareDenyWrite);
  if HFile = INVALID_HANDLE_VALUE then exit;
  Lo := GetFileSize(HFile, @Hi);
  if Lo<>$FFFFFF then
  begin
    if not ((INt64Rec(FileSize).Lo=Lo) and (INt64Rec(FileSize).Hi=Hi)) then
    begin
      FileClose(HFile);
      HFile := 0;
      Exit;
    end;
    if not _Manager._Controller._HashManager.GetHash(FName, FileMD5) then
      exit;
    if not MD5DigestCompare(FileMD5, FileHash) then
      Exit;
  end
  else
    if GetLastError=NO_ERROR then
    begin
      if not ((INt64Rec(FileSize).Lo=lo) and (INt64Rec(FileSize).Hi=Hi)) then
      begin
        FileClose(HFile);
        HFile := 0;
        Exit;
      end;
      if not _Manager._Controller._HashManager.GetHash(FName, FileMD5) then
        exit;
      if not MD5DigestCompare(FileMD5, FileHash) then
        Exit;
    end;
  if HFile <> 0 then
    FileClose(HFile);
  Synchronize(SendUpload);
end;

procedure TPKSTADDUPLOADThread.SendUpload;
begin
  _Manager._Controller.SendData(host,port,DownloadID,FileHash,FileSize,FileName, StartPos, RealFileName);
end;

{ TPKSTUpLoadManager }

procedure TPKSTUpLoadManager.AddTask(Task: TPKSTBasicTask);
begin
  _TaskList.Add(Task);
  if assigned(_Controller.FonUpLoadNotify) then
    _Controller.FonUpLoadNotify(self);
end;

procedure TPKSTUpLoadManager.AddThread(Thread: TPKSTADDUPLOADThread);
begin
  _ThreadList.Add(Thread);
end;

function TPKSTUpLoadManager.CanUpLoad: Boolean;
begin
  Result := _TaskList.Count+_ThreadList.Count < _Controller._MaxUpLoads;
end;

constructor TPKSTUpLoadManager.Create(Controller: TPKSTController);
begin
  _Controller := Controller;
  _ThreadList := TList.Create;
  _TaskList := TList.Create;
end;

procedure TPKSTUpLoadManager.DeleteTask(Task: TPKSTBasicTask);
var
  i:  Integer;
begin
  i := _TaskList.IndexOf(Task);
  if i>=0 then
    _TaskList.Delete(i);
  if assigned(_Controller.FonUpLoadNotify) then
    _Controller.FonUpLoadNotify(self);
end;

procedure TPKSTUpLoadManager.DeleteThread(Thread: TPKSTADDUPLOADThread);
var
  i:  Integer;
begin
  i := _ThreadList.IndexOf(Thread);
  if i>=0 then
    _ThreadList.Delete(i);
end;

destructor TPKSTUpLoadManager.Destroy;
var
  i: integer;
begin
  for i:= _ThreadList.Count-1 downto 0 do
    TPKSTADDUPLOADThread(_ThreadList.Items[i]).Terminate;
  _ThreadList.Free;

  for i:= _TaskList.Count-1 downto 0 do
    TPKSTBasicTask(_TaskList.Items[i]).Terminate;
  _TaskList.Free;
  inherited;
end;

function TPKSTUpLoadManager.GetUpLoadCount: Integer;
begin
  Result := _TaskList.Count;
end;

procedure TPKSTUpLoadManager.GetUpLoadInfo(idx: Integer;
  var FileName: string; var FileSize, DownloadSize: int64);
begin
  if idx>=UpLoadCount then exit;
  FileName := TPKSTDataTask(_TaskList.Items[idx]).FileName;
  FileSize := TPKSTDataTask(_TaskList.Items[idx]).FileSize;
  DownloadSize := TPKSTDataTask(_TaskList.Items[idx]).StartPos;
end;

{ TPKSTMsgTask }

constructor TPKSTMsgTask.Create(Manager: TPKSTTaskManager);
begin
  inherited;
end;

procedure TPKSTMsgTask.DoTimeOut;
begin
  inherited;
  if Node.LocalIP.S_addr= GStack.StringToTInAddr(Host).S_addr then
  begin
    Host := GStack.TInAddrToString(Node.IP);
    Port := Node.Port;
    DoSend;
  end
  else
    Terminate;
end;

procedure TPKSTMsgTask.Execute;
begin
  inherited;
end;

procedure TPKSTMsgTask.ProcReply(var Data; Size: Integer);
begin
  inherited;
  Terminate;
end;

{ TPKSTNodeManager }

function TPKSTNodeManager.CheckActiveNode(AHost: string;
  APort: Integer): Boolean;
var
  i: Integer;
  Ip: TIdInAddr;
  NodeInfo: TPKSTNodeInfo;
  NodeInfoEx: TPKSTNodeInfoEX;
begin
  Result := false;
  if _ServerMode then
  begin
    Ip := GStack.StringToTInAddr(AHost);
    for i:= ActiveNodes.Count-1 downto 0 do
    begin
      NodeInfo := TPKSTNodeInfo(ActiveNodes.Items[i]);
      if ((Ip.S_addr=NodeInfo.NodeAdd.IP.S_addr) and (APort=NodeInfo.NodeAdd.Port)) or
         ((Ip.S_addr=NodeInfo.NodeAdd.LocalIP.S_addr) and (APort=PKST_CLIENTPORT)) then
        Result := true;
      if Result then exit;
    end;
  end
  else
  begin
    try
      Ahost := GStack.ResolveHost(Ahost);
    except
      exit;
    end;

    if APort<>PKST_SERVERPORT then
      Exit;
    for i:= ActiveNodes.Count-1 downto 0 do
    begin
      Ip := GStack.StringToTInAddr(AHost);
      NodeInfoEx := TPKSTNodeInfoEX(ActiveNodes.Items[i]);
      if SameHost(NodeInfoEx.ServerHost, AHost) or
         (Ip.S_addr=NodeInfoEx.LocalIP.S_addr) then
        Result := true;
      if Result then exit;
    end;
  end;
end;

procedure TPKSTNodeManager.ConnTime(Sender: TObject);
var
  i: Integer;
  Tick: Cardinal;
  Dif: Cardinal;
  NodeInfo: TPKSTNodeInfo;
  NodeInfoEx: TPKSTNodeInfoEx;
begin
  Tick := GetTickCount;
  if _ServerMode then
  begin
    for i:= ActiveNodes.Count-1 downto 0 do
    begin
      NodeInfo := TPKSTNodeInfo(ActiveNodes.Items[i]);
      if NodeInfo.Lastresponse > Tick then
        Dif := Tick+($FFFFFFFF-NodeInfo.Lastresponse)
      else
        Dif := Tick-NodeInfo.Lastresponse;
      if Dif > PKST_CONN_LIVETIMEOUT*1000 then
        DeActive(NodeInfo);
    end;
  end
  else
  begin
    for i:= DeActiveNodes.Count-1 downto 0 do
    begin
      NodeInfoEx := TPKSTNodeInfoEx(DeActiveNodes.Items[i]);
      if NodeInfoEx.Lastresponse > Tick then
        Dif := Tick+($FFFFFFFF-NodeInfoEx.Lastresponse)
      else
        Dif := Tick-NodeInfoEx.Lastresponse;
      if Dif > PKST_CONN_TIMEOUT*1000 then
        ReConnectServer(NodeInfoEx);
    end;
    for i:= ActiveNodes.Count-1 downto 0 do
    begin
      NodeInfoEx := TPKSTNodeInfoEx(ActiveNodes.Items[i]);
      if NodeInfoEx.Lastresponse > Tick then
        Dif := Tick+($FFFFFFFF-NodeInfoEx.Lastresponse)
      else
        Dif := Tick-NodeInfoEx.Lastresponse;
      if Dif > PKST_CONN_LIVE*1000 then
        KeepServerAlive(NodeInfoEx);
      if Dif > PKST_CONN_LIVETIMEOUT*1000 then
        DeActive(NodeInfoEx);
    end;
  end;
end;

constructor TPKSTNodeManager.Create(Controller: TPKSTController);
begin
  inherited create;
  ActiveNodes := TObjectList.Create;
  DeActiveNodes := TObjectList.Create;
  _ServerList := TstringList.Create;
  _Controller := Controller;
  _ServerMode := Controller.ServerMode;
  _Timer := TTimer.Create(nil);
  _Timer.Interval := 1000;
  _Timer.OnTimer := ConnTime;
  _Timer.Enabled := False;
end;

procedure TPKSTNodeManager.DeActive(Node: TObject);
begin
  if ActiveNodes.IndexOf(Node)>=0 then
  begin
    DeActiveNodes.Add(ActiveNodes.Extract(Node));
    if Assigned(_Controller.FOnDisConnect) then
      _Controller.FOnDisConnect(_Controller);
  end;
end;

destructor TPKSTNodeManager.Destroy;
var
  i: Integer;
begin
  _Timer.Enabled := False;
  for i:= DeActiveNodes.Count-1 downto 0 do
  begin
    DeActiveNodes.Extract(DeActiveNodes.Items[i]).Free;
  end;
  DeActiveNodes.Free;

  for i:= ActiveNodes.Count-1 downto 0 do
  begin
    ActiveNodes.Extract(ActiveNodes.Items[i]).Free;
  end;
  ActiveNodes.Free;
  _ServerList.Free;
  inherited;
end;

function TPKSTNodeManager.GetActiveNode(i: Integer): TPKSTNodeInfo;
begin
  Result := nil;
  if _ServerMode then
  begin
    if i >= ActiveNodes.Count then exit;
    Result := TPKSTNodeInfo(ActiveNodes.Items[i]);
  end;
end;

function TPKSTNodeManager.GetActiveNodeByHost(AHost: string;
  APort: Integer): TPKSTNodeInfo;
var
  i: Integer;
  Ip: TIdInAddr;
  NodeInfo: TPKSTNodeInfo;
begin
  Result := nil;
  if not _ServerMode then exit;
  try
    Ahost := GStack.ResolveHost(Ahost);
  except
    exit;
  end;

  for i:= ActiveNodes.Count-1 downto 0 do
  begin
    Ip := GStack.StringToTInAddr(AHost);
    NodeInfo := TPKSTNodeInfo(ActiveNodes.Items[i]);
    if SameHost(GStack.TInAddrToString(NodeInfo.NodeAdd.IP), AHost) or
       (Ip.S_addr=NodeInfo.NodeAdd.LocalIP.S_addr) then
    begin
      Result := NodeInfo;
      exit;
    end;
  end;
end;

function TPKSTNodeManager.GetActiveNodeCount: Integer;
begin
  Result := 0;
  if _ServerMode then
    Result := ActiveNodes.Count;
end;

function TPKSTNodeManager.GetActiveServer(i: Integer): TPKSTNodeInfoEX;
begin
  Result := nil;
  if not _ServerMode then
  begin
    if i >= ActiveNodes.Count then exit;
    Result := TPKSTNodeInfoEx(ActiveNodes.Items[i]);
  end;
end;

function TPKSTNodeManager.GetActiveServerByHost(AHost: string;
  APort: Integer): TPKSTNodeInfoEX;
var
  i: Integer;
  Ip: TIdInAddr;
  NodeInfoEx: TPKSTNodeInfoEX;
begin
  Result := nil;
  if _ServerMode then exit;
  try
    Ahost := GStack.ResolveHost(Ahost);
  except
    exit;
  end;

  if APort<>PKST_SERVERPORT then
    Exit;
  for i:= ActiveNodes.Count-1 downto 0 do
  begin
    Ip := GStack.StringToTInAddr(AHost);
    NodeInfoEx := TPKSTNodeInfoEX(ActiveNodes.Items[i]);
    if SameHost(NodeInfoEx.ServerHost, AHost) or
       (Ip.S_addr=NodeInfoEx.LocalIP.S_addr) then
    begin
      Result := NodeInfoEx;
      exit;
    end;
  end;
end;

function TPKSTNodeManager.GetActiveServerCount: Integer;
begin
  Result := 0;
  if not _ServerMode then
    Result := ActiveNodes.Count;
end;

function TPKSTNodeManager.GetDeActiveNode(i: Integer): TPKSTNodeInfo;
begin
  Result := nil;
  if _ServerMode then
  begin
    if i >= DeActiveNodes.Count then exit;
    Result := TPKSTNodeInfo(DeActiveNodes.Items[i]);
  end;
end;

function TPKSTNodeManager.GetDeActiveNodeByHost(AHost: string;
  APort: Integer): TPKSTNodeInfo;
var
  i: Integer;
  Ip: TIdInAddr;
  NodeInfo: TPKSTNodeInfo;
begin
  Result := nil;
  if not _ServerMode then exit;
  try
    Ahost := GStack.ResolveHost(Ahost);
  except
    exit;
  end;

  for i:= DeActiveNodes.Count-1 downto 0 do
  begin
    Ip := GStack.StringToTInAddr(AHost);
    NodeInfo := TPKSTNodeInfo(DeActiveNodes.Items[i]);
    if SameHost(GStack.TInAddrToString(NodeInfo.NodeAdd.IP), AHost) or
       (Ip.S_addr=NodeInfo.NodeAdd.LocalIP.S_addr) then
    begin
      Result := NodeInfo;
      exit;
    end;
  end;
end;

function TPKSTNodeManager.GetDeActiveNodeCount: Integer;
begin
  Result := 0;
  if _ServerMode then
    Result := DeActiveNodes.Count;
end;

function TPKSTNodeManager.GetDeActiveServer(i: Integer): TPKSTNodeInfoEX;
begin
  Result := nil;
  if not _ServerMode then
  begin
    if i >= DeActiveNodes.Count then exit;
    Result := TPKSTNodeInfoEx(DeActiveNodes.Items[i]);
  end;
end;

function TPKSTNodeManager.GetDeActiveServerByHost(AHost: string;
  APort: Integer): TPKSTNodeInfoEX;
var
  i: Integer;
  Ip: TIdInAddr;
  NodeInfoEx: TPKSTNodeInfoEX;
begin
  Result := nil;
  if _ServerMode then exit;
  try
    Ahost := GStack.ResolveHost(Ahost);
  except
    exit;
  end;

  if APort<>PKST_SERVERPORT then
    Exit;
  for i:= DeActiveNodes.Count-1 downto 0 do
  begin
    Ip := GStack.StringToTInAddr(AHost);
    NodeInfoEx := TPKSTNodeInfoEX(DeActiveNodes.Items[i]);
    if SameHost(NodeInfoEx.ServerHost, AHost) or
       (Ip.S_addr=NodeInfoEx.LocalIP.S_addr) then
    begin
      Result := NodeInfoEx;
      exit;
    end;
  end;
end;

function TPKSTNodeManager.GetDeActiveServerCount: Integer;
begin
  Result := 0;
  if not _ServerMode then
    Result := DeActiveNodes.Count;
end;

function TPKSTNodeManager.GetServerAdd(I: Integer): string;
begin

end;

procedure TPKSTNodeManager.InitServerList(ServerList: Tstrings);
var
  i: Integer;
  idx: Integer;
  NodeInfoEX: TPKSTNodeInfoEX;
begin
  if _ServerMode then exit;
  if ServerList<>nil then
    _ServerList.Assign(ServerList)
  else
  begin
    for i := ActiveNodes.Count-1 downto 0 do
    begin
      TPKSTNodeInfoEX(ActiveNodes.Extract(ActiveNodes.Items[i])).Free
    end;

    for i := DeActiveNodes.Count-1 downto 0 do
    begin
      TPKSTNodeInfoEX(DeActiveNodes.Extract(DeActiveNodes.Items[i])).Free
    end;
    exit;
  end;

  for i := ActiveNodes.Count-1 downto 0 do
  begin
    idx := ServerList.IndexOf(TPKSTNodeInfoEX(ActiveNodes.Items[i]).ServerHost);
    if idx<0 then
      TPKSTNodeInfoEX(ActiveNodes.Extract(ActiveNodes.Items[i])).Free
    else
      ServerList.Delete(idx);
  end;

  for i := DeActiveNodes.Count-1 downto 0 do
  begin
    idx := ServerList.IndexOf(TPKSTNodeInfoEX(DeActiveNodes.Items[i]).ServerHost);
    if idx<0 then
      TPKSTNodeInfoEX(DeActiveNodes.Extract(DeActiveNodes.Items[i])).Free
    else
      ServerList.Delete(idx);
  end;

  for i := ServerList.Count-1 downto 0 do
  begin
    NodeInfoEX := TPKSTNodeInfoEX.Create;
    NodeInfoEX.ServerHost := ServerList[i];
    NodeInfoEX.Lastresponse := 0;
    DeActiveNodes.Add(NodeInfoEX);
  end;
end;

procedure TPKSTNodeManager.KeepServerAlive(Server: TPKSTNodeInfoEx);
begin
  _Controller.SendConnect(server.ServerHost, PKST_SERVERPORT);
end;

procedure TPKSTNodeManager.Open;
begin
  _Timer.Enabled := true;
end;

procedure TPKSTNodeManager.procNodeInfo(ANodeName: string; ANode: TPKSTNode;
  ANodeID: TGUID);
var
  IP: TIdInAddr;
  NewNode: TPKSTNodeInfo;
  i: integer;
  NodeInfoEx: TPKSTNodeInfoEx;
begin
  if _ServerMode then
  begin
    for i:= ActiveNodes.Count-1 downto 0 do
      with TPKSTNodeInfo(ActiveNodes.Items[i]) do
      if IsEqualGUID(NodeID, ANodeID) then
      begin
        NodeName := ANodeName;
        NodeAdd := ANode;
        Lastresponse := GetTickCount;
        exit;
      end;
    for i:= DeActiveNodes.Count-1 downto 0 do
      if IsEqualGUID(TPKSTNodeInfo(DeActiveNodes.Items[i]).NodeID, ANodeID) then
      begin
        NewNode :=  TPKSTNodeInfo(DeActiveNodes.Extract(TPKSTNodeInfo(DeActiveNodes.Items[i])));
        with NewNode do
        begin
          NodeName := ANodeName;
          NodeAdd := ANode;
          Lastresponse := GetTickCount;
        end;
        ActiveNodes.Add(NewNode);
        if Assigned(_Controller.FonConnect) then
          _Controller.FOnConnect(_Controller);
        exit;
      end;

    NewNode := TPKSTNodeInfo.Create;
    with NewNode do
    begin
      NodeID := ANodeID;
      NodeName := ANodeName;
      NodeAdd := ANode;
      Lastresponse := GetTickCount;
    end;
    ActiveNodes.Add(NewNode);
    if Assigned(_Controller.FonConnect) then
      _Controller.FOnConnect(_Controller);
  end
  else
  begin
    if ANode.Port<>PKST_SERVERPORT then
      exit;
    for i := DeActiveNodes.Count-1 downto 0 do
    begin
      NodeInfoEx := TPKSTNodeInfoEx(DeActiveNodes.Items[i]);
      if IsEqualGUID(NodeInfoEx.ServerID, ANodeID) or
         SameHost(NodeInfoEx.ServerHost, GStack.TInAddrToString(ANode.IP)) then
      begin
        NodeInfoEx.ServerID := ANodeID;
        NodeInfoEx.LocalIP := ANode.LocalIP;
        NodeInfoEx.ServerName := ANodeName;
        NodeInfoEx.Lastresponse := GetTickCount;
        ActiveNodes.Add(DeActiveNodes.Extract(NodeInfoEx));
        exit;
      end;
    end;
  end;
end;

procedure TPKSTNodeManager.ReConnectServer(Server: TPKSTNodeInfoEx);
begin
  server.Lastresponse := GetTickCount;
  _Controller.SendConnect(server.ServerHost, PKST_SERVERPORT);
end;

procedure TPKSTNodeManager.Stop;
var
  i: Integer;
begin
  _Timer.Enabled := False;
  for i := ActiveNodes.Count-1 downto 0 do
    DeActive(ActiveNodes.Items[i]);
end;

procedure TPKSTNodeManager.UpdateConnect(AHost: string; APort: Integer);
var
  i: Integer;
  Ip: TIdInAddr;
  NodeInfo: TPKSTNodeInfo;
  NodeInfoEx: TPKSTNodeInfoEx;
begin
  if _ServerMode then
  begin
    Ip := GStack.StringToTInAddr(AHost);
    for i:= ActiveNodes.Count-1 downto 0 do
    begin
      NodeInfo := TPKSTNodeInfo(ActiveNodes.Items[i]);
      if ((Ip.S_addr=NodeInfo.NodeAdd.IP.S_addr) and (APort=NodeInfo.NodeAdd.Port)) or
         ((Ip.S_addr=NodeInfo.NodeAdd.LocalIP.S_addr) and (APort=PKST_CLIENTPORT)) then
      begin
        NodeInfo.Lastresponse := GetTickCount;
        exit;
      end;
    end;
  end
  else
  begin
    if APort<>PKST_SERVERPORT then
      exit;
    Ip := GStack.StringToTInAddr(AHost);
    for i := ActiveNodes.Count-1 downto 0 do
    begin
      NodeInfoEx := TPKSTNodeInfoEx(ActiveNodes.Items[i]);
      if SameHost(NodeInfoEx.ServerHost, AHost) or
         (NodeInfoEx.LocalIP.S_addr = IP.S_addr) then
      begin
        NodeInfoEx.Lastresponse := GetTickCount;
        exit;
      end;
    end;
    _Controller.GetNodeInfo(AHost,APort);
  end;
end;

{ TPKSTHashManager }

function TPKSTHashManager.CalcNewHash(FileName: string): TMD5Digest;
var
  hFile: THandle;
  FI: TByHandleFileInformation;
  HashData: TPKSTHashData;
begin
  if _HashThread<>nil then
    _HashThread.Suspend;
  if not FileExists(FileName) then
    exit;
  hFile := FileOpen(FileName, fmOpenRead or fmShareDenyNone);
  if hFile= INVALID_HANDLE_VALUE then
    exit;
  GetFileInformationByHandle(hFile, fi);
  FileClose(hFile);
  HashData.FileName := FileName;
  HashData.FileSizeRec.Hi := Fi.nFileSizeHigh;
  HashData.FileSizeRec.Lo := Fi.nFileSizeLow;
  HashData.FileTime := fi.ftLastWriteTime;
  HashData.FileHash := MD5File(FileName);
  CheckInHash(HashData);
  Result := HashData.FileHash;
  if _HashThread<>nil then
    _HashThread.Resume;
end;

procedure TPKSTHashManager.CheckInHash(FileName: string; FileSize: Int64;
  FileTime: TFileTime; FileHash: TMD5Digest);
var
  i: Integer;
begin
  for i := _HashCount-1 downto 0 do
  begin
    if _HashPool[i].FileName = FileName then
    begin
      _HashPool[i].FileSize := FileSize;
      _HashPool[i].FileTime := FileTime;
      _HashPool[i].FileHash := FileHash;
      exit;
    end;
  end;
  if length(_HashPool)<=_HashCount then
    setlength(_HashPool, _HashCount+64);
  _HashPool[_HashCount].FileName := FileName;
  _HashPool[_HashCount].FileSize := FileSize;
  _HashPool[_HashCount].FileTime := FileTime;
  _HashPool[_HashCount].FileHash := FileHash;
  _HashCount := _HashCount+1;
end;

procedure TPKSTHashManager.CheckInHash(HashData: TPKSTHashData);
begin
  CheckInHash(HashData.FileName, HashData.FileSize, HashData.FileTime, HashData.FileHash);
end;

constructor TPKSTHashManager.Create(Controller: TPKSTController);
begin
  _Controller := Controller;
  _HashThread := nil;
end;

destructor TPKSTHashManager.Destroy;
begin
  if _HashThread<>nil then
    stop
  else
    Save;
  inherited;
end;

function TPKSTHashManager.GetHash(FileName: string;
  var Hash: TMD5Digest): Boolean;
var
  i: Integer;
  hFile: THandle;
  FI: TByHandleFileInformation;
begin
  Result := false;
  if not FileExists(FileName) then
    exit;
  hFile := FileOpen(FileName, fmOpenRead or fmShareDenyNone);
  if hFile= INVALID_HANDLE_VALUE then
    exit;
  GetFileInformationByHandle(hFile, fi);
  FileClose(hFile);
  for i:= _HashCount-1 downto 0 do
  begin
    if _HashPool[i].FileName=FileName then
    begin
      if _HashPool[i].FileSizeRec.Hi<>Fi.nFileSizeHigh then exit;
      if _HashPool[i].FileSizeRec.Lo<>Fi.nFileSizeLow then exit;
      if CompareFileTime(_HashPool[i].FileTime, Fi.ftLastWriteTime)<>0 then exit;
      Result := true;
      Hash := _HashPool[i].FileHash;
      exit;
    end;
  end
end;

procedure TPKSTHashManager.open;
begin
  _HashThread := TPKSTHashThread.Create(Self);
  _HashThread.Resume;
end;

procedure TPKSTHashManager.Save;
var
  i: Integer;
  sl: TStringList;
begin
  if _HashCount<=0 then exit;
  if _HashThread<>nil then exit;
  sl := TStringList.Create;
  for i := _HashCount-1 downto 0 do
  begin
    sl.Add(_HashPool[i].FileName);
    sl.Add(IntToStr(_HashPool[i].FileSize));
    sl.Add(IntToStr(_HashPool[i].FileTimeInt64));
    sl.Add(MD5DigestToStr(_HashPool[i].FileHash));
  end;
  sl.SaveToFile(FCacheFileName);
  sl.Free;
end;

procedure TPKSTHashManager.stop;
begin
  if _HashThread <> nil then
    _HashThread.Terminate;
end;

{ TPKSTHashThread }

procedure TPKSTHashThread.AddHashData;
begin
  _Manager.CheckInHash(_CurHashData);
end;

constructor TPKSTHashThread.Create(Manager: TPKSTHashManager);
begin
  inherited Create(True);
  FreeOnTerminate := True;
  Priority := tpLowest;
  _Manager := Manager;
end;

destructor TPKSTHashThread.Destroy;
begin
  _Manager._HashThread := nil;
  inherited;
end;

procedure TPKSTHashThread.Execute;
var
  i: Integer;
  sl: TStringList;
  hFile: THandle;
  FileInformation: TByHandleFileInformation;
  filename: string;
  filesize: int64;
  filetime: TFileTime;
  filehash: TMD5Digest;

begin
  inherited;
  sl := TStringList.Create;
  try
    if FileExists(_Manager.CacheFileName) then
      sl.LoadFromFile(_Manager.CacheFileName);

    //首先将无变化的文件hash存入
    for i := (sl.Count div 4)-1 downto 0 do
    begin
      if Terminated then
        exit;
      if FileExists(sl[i*4]) then
      begin
        filename := sl[i*4];
        filesize := StrToInt64(sl[i*4+1]);
        int64(filetime) := StrToInt64(sl[i*4+2]);
        filehash := StrToMD5Digest(sl[i*4+3]);
        hFile := FileOpen(filename, fmOpenRead or fmShareDenyNone);
        if hFile<> INVALID_HANDLE_VALUE then
        begin
          GetFileInformationByHandle(hFile, FileInformation);
          FileClose(hFile);
          if (FileInformation.nFileSizeHigh = Int64rec(filesize).Hi) and (FileInformation.nFileSizeLow = Int64rec(filesize).Lo) and (CompareFileTime(FileInformation.ftLastWriteTime, filetime)=0) then
          begin
            _CurHashData.FileName := filename;
            _CurHashData.FileSize := filesize;
            _CurHashData.FileTime := filetime;
            _CurHashData.FileHash := filehash;
            Synchronize(AddHashData);
            sl.Delete(i*4+3);
            sl.Delete(i*4+2);
            sl.Delete(i*4+1);
            sl.Delete(i*4);
            Continue;
          end;
        end;
      end;
    end;

    //计算有异动的文件的Hash并加入
    for i := (sl.Count div 4)-1 downto 0 do
    begin
      if Terminated then
        exit;
      if FileExists(sl[i*4]) then
      begin
        _CurHashData.FileName := sl[i*4];
        hFile := FileOpen(_CurHashData.FileName, fmOpenRead or fmShareDenyNone);
        if hFile<> INVALID_HANDLE_VALUE then
        begin
          GetFileInformationByHandle(hFile, FileInformation);
          FileClose(hFile);
          _CurHashData.FileSizeRec.Lo := FileInformation.nFileSizeLow;
          _CurHashData.FileSizeRec.Hi := FileInformation.nFileSizeHigh;
          _CurHashData.FileTime := FileInformation.ftLastWriteTime;
          _CurHashData.FileHash := MD5File(_CurHashData.FileName);
          Synchronize(AddHashData);
          Continue;
        end;
      end;
    end;
  finally
    sl.Free;
  end;
  Synchronize(SaveHashData);
end;

procedure TPKSTHashThread.SaveHashData;
begin
  _Manager.Save;
end;

{ TPKSTPublishSubTitleTask }

constructor TPKSTPublishSubTitleTask.Create(Manager: TPKSTTaskManager);
begin
  inherited;
  CurNode := 0;
end;

procedure TPKSTPublishSubTitleTask.DoTimeOut;
begin
  inherited;
  Terminate;
end;

procedure TPKSTPublishSubTitleTask.Execute;
begin
  Synchronize(Publish);
  while not Terminated do
  begin
    if Now-LastResponseTime > TimeOut then
      DoTimeOut;
    Sleep(550);
  end;
end;

procedure TPKSTPublishSubTitleTask.ProcReply(var Data; Size: Integer);
begin
  inherited;
  Terminate;
end;

procedure TPKSTPublishSubTitleTask.Publish;
var
  PKSTHead: TPKSTHead;
  Guid: TGUID;
  M: TMemoryStream;
begin
  CreateGUID(Guid);
  TaskID := GUID;
  PKSTHead.Flag := PKSTFLAG;
  PKSTHead.Version := PKSTVERSION;
  PKSTHead.DataType := PKST_SUBTITLE;
  M := TMemoryStream.Create;
  M.Write(PKSTHead, Sizeof(TPKSTHead));
  M.Write(Guid, Sizeof(TGUID));
  M.Write(SubTitle[1], Length(SubTitle));
  PKSTHead.DataSize := M.Size-Sizeof(TPKSTHead);
  m.Seek(0,0);
  M.Write(PKSTHead, Sizeof(TPKSTHead));
  m.Seek(0,0);
  setlength(SendData, M.Size);
  M.Read(SendData[0],M.Size);
  SendSize := M.size;
  M.Free;
  Host := GStack.TInAddrToString(PubNodes[CurNode].IP);
  Port := PubNodes[CurNode].Port;
  DoSend;
end;

procedure TPKSTPublishSubTitleTask.PublishNext;
begin
  if CurNode<High(PubNodes) then
  begin
    CurNode := CurNode + 1;
    if Assigned(FonPublish) then
      FonPublish(self);
    Publish;
  end
  else
  begin
    if Assigned(FonEndPublish) then
      FonEndPublish(self);
    Terminate;
    exit;
  end;
end;

{ TPKSTTCPController }

constructor TPKSTTCPController.Create(Controller: TPKSTController);
begin
  _Controller := Controller;
  _socket := TIdTCPClient.Create(nil);
  _thread := TPKSTTCPThread.Create(_socket);

end;

destructor TPKSTTCPController.Destroy;
begin
  _thread.Terminate;
  _socket.Free;
  inherited;
end;

procedure TPKSTTCPController.open(CenterHost: string; CenterPort: Integer = 80);
begin
  try
    _socket.Connect;
    _thread.Resume;
  except

  end;
end;

procedure TPKSTTCPController.Stop;
begin
  _thread.Suspend;
  _socket.Disconnect;
end;

{ TPKSTTCPThread }

constructor TPKSTTCPThread.Create(socket: TIdTCPClient);
begin
  inherited Create(True);
  FreeOnTerminate := true;
  _socket := socket;
end;

destructor TPKSTTCPThread.Destroy;
begin

  inherited;
end;

procedure TPKSTTCPThread.Run;
begin
  inherited;

end;

end.
