【问题标题】:Delphi Android App INDY TCP Server - Application closes after client disconnectDelphi Android App INDY TCP Server - 客户端断开连接后应用程序关闭
【发布时间】:2015-09-22 16:37:53
【问题描述】:

我非常需要帮助。

我有一个基于 Indy 10 的 TIdTCPServer 组件的 TCP 服务器应用程序,我想在 Win32 和 Android 上运行。我正在使用 Delphi XE7。

服务器必须处理 10 个客户端。

  • 应用程序在 Windows 和 Android 上运行良好:CONNECTING、SENDING、RECEIVING 数据,但OnDisconnect 事件仅在 Android 上存在问题。该应用程序在 Windows 上运行良好,但在 Android 上断开客户端和事件时存在很大问题:TCPServer.Active := FALSE。在 90% 的情况下,当我断开客户端连接时,应用程序会自动关闭。

  • 当我启动服务器时:TCPServer1.Active := TRUE,然后我关闭它TCPServer1.Active := FALSE,没有连接客户端,应用程序工作正常。

我在下面添加我的代码。我使用了 Remy Lebeau 的提示。

  • 我正在使用两个客户端测试应用程序。
  • 我在 ListView 中显示连接的客户端。
  • 我不是从服务器事件中更新 ListView,而是在 Timer 事件中。
  • App 有 3 个按钮:Server Listen、Server Close、Send(在 ListView 中选择的客户端编号)

请帮忙。

//  TMyContext
constructor TMyContext.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil);
begin
  inherited;
  FQueue := TIdThreadSafeStringList.Create;
  FEvent := TEvent.Create(nil, True, False, '');
end;

destructor TMyContext.Destroy;
begin
  FQueue.Free;
  FEvent.Free;
  inherited;
end;

procedure TMyContext.AddMsgToQueue(const Msg: String);
begin
  with FQueue.Lock do
  try
    Add(Msg);
    FEvent.SetEvent;
  finally
    FQueue.Unlock;
  end;
end;

function TMyContext.GetQueuedMsgs: TStrings;
var
  List: TStringList;
begin
  Result := nil;
  if FEvent.WaitFor(1000) <> wrSignaled then Exit;
  List := FQueue.Lock;
  try
    if List.Count > 0 then
    begin
      Result := TStringList.Create;
      try
        Result.Assign(List);
        List.Clear;
      except
        Result.Free;
        raise;
      end;
    end;
    FEvent.ResetEvent;
  finally
    FQueue.Unlock;
  end;
end;

//  TCPServer

procedure THeaderFooterwithNavigation.TCPServer1Connect(AContext: TIdContext);
var
  client    : String;
  datetime  : TDateTime;
begin
  datetime := now;

  //  CLIENT CON INFO
  client := AContext.Binding.PeerIP;

  TThread.Queue(nil,
    procedure
    begin
      TCPServer1.Contexts.LockList();
      mmoLog.Lines.Add ('CONNECT:   ' + AContext.Connection.Socket.Binding.PeerIP
                        + ' : ' +
                        IntToStr(AContext.Connection.Socket.Binding.PeerPort) + '   ' +
            DateToStr (datetime) + '   ' + TimeToStr (datetime)
      );
      TCPServer1.Contexts.UnlockList();

      if TCPServer1.Contexts.Count = 1 then
        edtPort1.Text := IntToStr(AContext.Connection.Socket.Binding.PeerPort);

      if TCPServer1.Contexts.Count = 2 then
        edtPort2.Text := IntToStr(AContext.Connection.Socket.Binding.PeerPort);

      AContext.Connection.Socket.Binding.Send('HELLO');

      //  CLIENTSDATA LIST
      ClientsList.Add (' ', AContext.Connection.Socket.Binding.PeerIP, AContext.Connection.Socket.Binding.PeerPort);
      LV_Refresh ();
    end
  );
end;

procedure THeaderFooterwithNavigation.TCPServer1Disconnect(
  AContext: TIdContext);
var
  cl_item   :   Integer;
  datetime  :   TDateTime;
begin
  try
    datetime  := now;

    if  fSvrClose  =  FALSE then begin
      fClDiscon       := TRUE;
      buff_discon [pos_ip]    := AContext.Connection.Socket.Binding.PeerIP;
      buff_discon [pos_port]  := IntToStr (AContext.Connection.Socket.Binding.PeerPort);
      buff_discon [pos_date]  := DateToStr (datetime);
      buff_discon [pos_time]  := TimeToStr (datetime);
    end;
  finally
    AContext.Connection.Socket.InputBuffer.Clear;
    AContext.Connection.Disconnect;
  end;
end;

procedure THeaderFooterwithNavigation.TCPServer1Exception(AContext: TIdContext;
  AException: Exception);
begin
  ShowMessage ('Error');
end;

procedure THeaderFooterwithNavigation.TCPServer1Execute(AContext: TIdContext);
var
  buff    :   String;
  List    :   TStrings;
  I       :   Integer;
  buffout :   String;
  n       :   Integer;

  //  FOR DISCONNECT
  {$IFDEF MSWINDOWS}
  clist   :   TList;
  {$ENDIF MSWINDOWS}

  {$IFDEF Android}
  clist   :   TList <TIdContext>;
  {$ENDIF Android}

begin
  if  fSvrClose = FALSE  then begin
    //  READ MESSAGES FROM THE CLIENTS
    fDisconAccess := FALSE;

    //  SEND MESSAGES TO THE CLIENTS
    List := TMyContext(AContext).GetQueuedMsgs;

    if List <> nil then begin
      try
        for I := 0 to List.Count-1 do
          AContext.Connection.IOHandler.Write(List[I]);
      finally
        List.Free;
      end;
    end;

    //  READ MESSAGE FROM CLIENTS
    if  AContext.Connection.IOHandler.CheckForDataOnSource(200) then begin
      buffout := AContext.Connection.IOHandler.ReadLn();

      TThread.Queue(nil,
        procedure
        begin
          if  AContext.Connection.Socket.Binding.PeerPort = StrToInt(edtPort1.Text) then begin
            edtRec1.Text := buffout;
          end;

          if AContext.Connection.Socket.Binding.PeerPort = StrToInt(edtPort2.Text) then begin
            edtRec2.Text := buffout;
          end;

        end
      );
    end;
    fDisconAccess := TRUE;
  end;
end;

//  USER INTERFACE

procedure THeaderFooterwithNavigation.SendMessage (const IP : String; port : Word; Msg: string);
var
  I: Integer;
begin
  with TCPServer1.Contexts.LockList do
  try
    for I := 0 to Count-1 do begin
      with TMyContext(Items[I]) do begin
        if  (Binding.PeerIP = IP)  and (Binding.PeerPort = port)  then begin
          AddMsgToQueue(Msg);
          Break;
        end;
      end;
    end;
  finally
    TCPServer1.Contexts.UnlockList;
  end;
end;

procedure THeaderFooterwithNavigation.Timer1Timer(Sender: TObject);
begin
  Get_ClientsNum ();

  //  UPDATE UI (USER INTERFACE)
  UpdateUI ();

  //  BUTTONS
  if TCPServer1.Active = TRUE then begin
    btnListen.Enabled := FALSE;
    edtStatus.Text    := 'LISTENING';
  end else begin
    btnListen.Enabled := TRUE;
    edtStatus.Text    := 'CLOSED';
  end;

  //  ON SINGLE CLIENT DISCONNECT
  if  fClDiscon  =  TRUE  then begin
    fClDiscon := FALSE;
    CL_DeleteClient (buff_discon [pos_ip], StrToInt (buff_discon [pos_port]));
    LV_Refresh ();
    mmoLog.Lines.Add ('DISCON:   ' + buff_discon [pos_ip] + ' : ' + buff_discon [pos_port] + '   ' +
    buff_discon [pos_date] + '   ' + buff_discon [pos_time] );
  end;
end;

procedure THeaderFooterwithNavigation.TitleActionUpdate(Sender: TObject);
begin
  if Sender is TCustomAction then
  begin
    if TabControl1.ActiveTab <> nil then
      TCustomAction(Sender).Text := TabControl1.ActiveTab.Text
    else
      TCustomAction(Sender).Text := '';
  end;
end;

procedure THeaderFooterwithNavigation.btnCloseClick(Sender: TObject);
var
  {$IFDEF MSWINDOWS}
  clist     : TList;
  {$ENDIF MSWINDOWS}

  {$IFDEF Android}
  clist     : TList <TIdContext>;
  {$ENDIF Android}

  i         : Integer;
  ip        : String;
  port      : Word;
  datetime  : TDateTime;
begin
  TThread.Queue(nil,
    procedure
    var
      n : Integer;
    begin
      datetime  := now;

      if  Clients_Num  =  0  then begin
        TCPServer1.StopListening();
        TCPServer1.Active := FALSE;
      end else begin
        fSvrClose         := TRUE;

        //  SERVER CLOSE
        if fSvrClose  =  TRUE then begin
          while fDisconAccess = FALSE do begin
        end;

        try
          clist := TCPServer1.Contexts.LockList;
          for  n := 0  to  (clist.Count - 1) do begin
            try
              TIdContext (clist[n]).Connection.Socket.WriteBufferClear;
              TIdContext (clist[n]).Connection.Socket.InputBuffer.Clear;
              ip    := TIdContext (clist[n]).Connection.Socket.Binding.PeerIP;
              port  := TIdContext (clist[n]).Connection.Socket.Binding.PeerPort;
              TIdContext (clist[n]).Connection.Disconnect;
              CL_DeleteClient (ip, port);
              mmoLog.Lines.Add ('DISCON:   ' + ip + ' : ' + IntToStr(port) + '   ' +
              DateToStr (datetime) + '   ' + TimeToStr (datetime) );

              sleep (100);
            except
            end;
          end;
        finally
          TCPServer1.Contexts.UnlockList;
          TCPServer1.Active := FALSE;
          fSvrClose         := FALSE;
          LV_Refresh ();
        end;
      end;
    end
  );
end;

procedure THeaderFooterwithNavigation.btnListenClick(Sender: TObject);
var
  port : Word;
begin
  port := StrToInt (edtPort.Text);
  TCPServer1.Contexts.Clear;
  TCPServer1.Bindings.Clear();

  if (port > 200)  and  (port < 65535)  then begin
    TCPServer1.DefaultPort := StrToInt (edtPort.Text);
  end else
    TCPServer1.DefaultPort := 30000;

  TCPServer1.Bindings.Add.IPVersion := Id_IPv4;

  if  TCPServer1.Active  =  FALSE  then begin
    TCPServer1.Active := TRUE;
  end;
end;

procedure THeaderFooterwithNavigation.btnSendClick(Sender: TObject);
var
  ip    : string;
  port  : Word;
  item  : Integer;
begin
  item := LV.ItemIndex;
  if  (item  >  -1)  then begin
    ip    := ClientsList.Items[item].IP;
    port  := ClientsList.Items[item].Port;
    SendMessage (ip, port, edtSend.Text);
  end;
end;

procedure THeaderFooterwithNavigation.Get_ClientsNum ();
var
  {$IFDEF MSWINDOWS}
  clist : TList;
  {$ENDIF MSWINDOWS}

  {$IFDEF Android}
  clist : TList <TIdContext>;
  {$ENDIF Android}
begin
  try
    clist := TCPServer1.Contexts.LockList();
    Clients_Num := TCPServer1.Contexts.Count;
  finally
    TCPServer1.Contexts.UnlockList;
  end;
end;

【问题讨论】:

    标签: android delphi delphi-xe7 indy10


    【解决方案1】:

    对于 Windows 或 Android,此代码不正确或不安全。它完全起作用的事实是纯属运气。这段代码中有很多危险的逻辑需要重写。

    尝试类似的方法:

    //  TMyContext
    
    constructor TMyContext.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil);
    begin
      inherited;
      FQueue := TIdThreadSafeStringList.Create;
      FEvent := TEvent.Create(nil, True, False, '');
    end;
    
    destructor TMyContext.Destroy;
    begin
      FQueue.Free;
      FEvent.Free;
      inherited;
    end;
    
    procedure TMyContext.AddMsgToQueue(const Msg: String);
    begin
      with FQueue.Lock do
      try
        Add(Msg);
        FEvent.SetEvent;
      finally
        FQueue.Unlock;
      end;
    end;
    
    function TMyContext.GetQueuedMsgs: TStrings;
    var
      List: TStringList;
    begin
      Result := nil;
      if FEvent.WaitFor(1000) <> wrSignaled then Exit;
      List := FQueue.Lock;
      try
        if List.Count > 0 then
        begin
          Result := TStringList.Create;
          try
            Result.Assign(List);
            List.Clear;
          except
            Result.Free;
            raise;
          end;
        end;
        FEvent.ResetEvent;
      finally
        FQueue.Unlock;
      end;
    end;
    
    //  TCPServer
    
    procedure THeaderFooterwithNavigation.LogMessage(Msg: string);
    begin
      TThread.Queue(nil,
        procedure
        begin
          mmoLog.Lines.Add (Msg);
        end
      );
    end;
    
    procedure THeaderFooterwithNavigation.TCPServer1Connect(AContext: TIdContext);
    var
      clientIP  : String;
      clientPort: TIdPort;
      datetime  : TDateTime;
    begin
      datetime := now;
    
      //  CLIENT CON INFO
      clientIP := AContext.Binding.PeerIP;
      clientPort := AContext.Binding.PeerPort;
    
      AContext.Connection.IOHandler.WriteLn('HELLO');
    
      LogMessage('CONNECT:   ' + clientIP + ' : ' + IntToStr(clientPort) + '   ' + DateToStr (datetime) + '   ' + TimeToStr (datetime));
    
      TThread.Queue(nil,
        procedure
        var
          client: string;
        begin
          client := clientIP + ':' + IntToStr(clientPort);
    
          case TCPServer1.Contexts.Count of
            1: edtPort1.Text := client;
            2: edtPort2.Text := client;
          end;
    
          //  CLIENTSDATA LIST
          ClientsList.Add (' ', clientIP, clientPort);
          LV_Refresh;
        end
      );
    end;
    
    procedure THeaderFooterwithNavigation.TCPServer1Disconnect(
      AContext: TIdContext);
    var
      datetime  :   TDateTime;
      clientIP  : String;
      clientPort: TIdPort;
    begin
      datetime  := now;
    
      //  CLIENT CON INFO
      clientIP := AContext.Binding.PeerIP;
      clientPort := AContext.Binding.PeerPort;
    
      LogMessage('DISCON:   ' + clientIP + ' : ' + IntToStr(clientPort) + '   ' + DateToStr(datetime)  + '   ' + TimeToStr(datetime));
    
      TThread.Queue(nil,
        procedure
        var
          client: string;
        begin
          client := clientIP + ':' + IntToStr(clientPort);
    
          if edtPort1.Text = client then begin
            edtPort1.Text := '';
          end;
    
          if edtPort2.Text = client then begin
            edtPort2.Text := '';
          end;
    
          CL_DeleteClient (clientIP, clientPort);
          if fSvrClose = FALSE then LV_Refresh;
        end
      );
    end;
    
    procedure THeaderFooterwithNavigation.TCPServer1Exception(AContext: TIdContext; AException: Exception);
    begin
      if fSvrClose = FALSE then
        LogMessage ('Error: ' + AException.Message);
    end;
    
    procedure THeaderFooterwithNavigation.TCPServer1Execute(AContext: TIdContext);
    var
      buff    :   String;
      List    :   TStrings;
      I       :   Integer;
      clientIP:   String;
      clientPort: TIdPort;
    begin
      if fSvrClose = TRUE then Exit;
    
      //  SEND MESSAGES TO THE CLIENTS
      List := TMyContext(AContext).GetQueuedMsgs;
    
      if List <> nil then
      try
        for I := 0 to List.Count-1 do
          AContext.Connection.IOHandler.WriteLn(List[I]);
      finally
        List.Free;
      end;
    
      if fSvrClose = TRUE then Exit;
    
      //  READ MESSAGE FROM CLIENTS
      if AContext.Connection.IOHandler.InputBufferIsEmpty then begin
        AContext.Connection.IOHandler.CheckForDataOnSource(200);
        AContext.Connection.IOHandler.CheckForDisconnect;
        if fSvrClose = TRUE then Exit;
      end;
    
      if not AContext.Connection.IOHandler.InputBufferIsEmpty then begin
      begin
        buff := AContext.Connection.IOHandler.ReadLn;
        if fSvrClose = TRUE then Exit;
    
        clientIP := AContext.Binding.PeerIP;
        clientPort := AContext.Binding.PeerPort;
    
        TThread.Queue(nil,
          procedure
          var
            client: string;
          begin
            client := clientIP + ':' + IntToStr(clientPort);
    
            if edtPort1.Text = client then begin
              edtRec1.Text := buff;
            end;
    
            if edtPort2.Text = client then begin
              edtRec2.Text := buff;
            end;
          end
        );
      end;
    end;
    
    //  USER INTERFACE
    
    procedure THeaderFooterwithNavigation.SendMessage (const IP : String; port : TIdPort; const Msg: string);
    var
      I: Integer;
    begin
      with TCPServer1.Contexts.LockList do
      try
        for I := 0 to Count-1 do begin
          with TMyContext(Items[I]) do begin
            if (Binding <> nil) and (Binding.PeerIP = IP)  and (Binding.PeerPort = port) then begin
              AddMsgToQueue(Msg);
              Exit;
            end;
          end;
        end;
      finally
        TCPServer1.Contexts.UnlockList;
      end;
    end;
    
    procedure THeaderFooterwithNavigation.Timer1Timer(Sender: TObject);
    begin
      Get_ClientsNum;
    
      //  UPDATE UI (USER INTERFACE)
      UpdateUI;
    end;
    
    procedure THeaderFooterwithNavigation.TitleActionUpdate(Sender: TObject);
    begin
      if Sender is TCustomAction then
      begin
        if TabControl1.ActiveTab <> nil then
          TCustomAction(Sender).Text := TabControl1.ActiveTab.Text
        else
          TCustomAction(Sender).Text := '';
      end;
    end;
    
    procedure THeaderFooterwithNavigation.btnCloseClick(Sender: TObject);
    begin
      fSvrClose := TRUE;
    
      // SERVER CLOSE
      TCPServer1.Active := FALSE;
      btnListen.Enabled := TRUE;
      edtStatus.Text    := 'CLOSED';
    
      fSvrClose := FALSE;
      LV_Refresh;
    end;
    
    procedure THeaderFooterwithNavigation.btnListenClick(Sender: TObject);
    var
      port : TIdPort;
    begin
      port := StrToInt (edtPort.Text);
    
      TCPServer1.Active := False;
      TCPServer1.Bindings.Clear;
    
      if (port > 200)  and  (port < 65535)  then begin
        TCPServer1.DefaultPort := port;
      end else
        TCPServer1.DefaultPort := 30000;
    
      TCPServer1.Bindings.Add.IPVersion := Id_IPv4;
      TCPServer1.Active := TRUE;
    
      btnListen.Enabled := FALSE;
      edtStatus.Text    := 'LISTENING';
    end;
    
    procedure THeaderFooterwithNavigation.btnSendClick(Sender: TObject);
    var
      ip    : string;
      port  : Word;
      item  : Integer;
    begin
      item := LV.ItemIndex;
      if (item > -1) then begin
        ip    := ClientsList.Items[item].IP;
        port  := ClientsList.Items[item].Port;
        SendMessage (ip, port, edtSend.Text);
      end;
    end;
    
    procedure THeaderFooterwithNavigation.Get_ClientsNum;
    begin
      Clients_Num := TCPServer1.Contexts.Count;
    end;
    

    【讨论】:

    • 感谢 Remy Lebeau 的回答。你有权利。我的代码是如此,因为我尝试了很多不同的组合,这是我第一个使用 INDY 的应用程序。我已经用你的代码提示更新了我的应用程序。它运行平稳、快速、稳定……但仍然存在同样的问题。在 80% 的情况下,当我断开 Android 设备上的一个/多个客户端时,应用程序会自动关闭。我在两个不同的平板电脑上测试它,它的工作原理是一样的。我已经删除了与 ListView 相关的函数,没有更好的了。我的问题可以与备忘录或编辑有关吗?
    • 在 INDY 中是否有可能应用程序正在访问客户端线程,而实际上它正在断开连接?
    • 我在这段代码中看不到任何可以在客户端断开连接时终止应用程序的内容。因此,必须有其他东西正在扼杀该应用程序。例如,UpdateUI() 实际上做了什么?您没有显示该代码。为什么要使用计时器来更新 UI,而不是在实际需要更改时使用事件或排队方法来更新 UI?
    • 超级!我有答案了!问题出在Android方面。在绝望的情况下,我试图在我的手机上安装我的应用程序在 Android 5.1 Lollipop 上,一切都很完美!以前的平板电脑适用于 Android Jelly Bean。我认为这就是问题所在。 Delphi 可以自动切换到正确的 SDK 还是我必须手动切换?我现在正在为 Jelly Bean 安装 SKD,我将尝试再次运行它。非常感谢 Remy 的专业帮助!我将在这里写下 SDK 安装后它是如何工作的。最好的问候。
    • 这太不可思议了。我仍然有同样的问题。我尝试使用大多数 SDK API 版本构建我的应用程序。现在,我已经安装了 API:10、15、16、17、18、19、21 和 23。我已经使用所有这些 API 版本构建了我的应用程序,但什么也没有!该应用程序仅适用于 Android 5.0 (API 21) 的设备,并且可以使用 API 15-23 构建。该应用无法在搭载 Android 4.2.1 (API 17) 和 Android 4.1 (API 16) 的设备上正常运行。仍然存在同样的问题。应用程序在客户端断开事件中被杀死。我在以下位置设置 SDK:工具 > 选项 > SDK 管理器并添加。请帮忙!
    【解决方案2】:

    几乎每次断开客户端/客户端并停用服务器时都会发生此问题。断开/停用事件进展顺利的情况有几种,但仅在连接一个客户端时。当连接的客户端很少时,断开连接和停用事件总是出错。即使禁用了所有 UI 功能并且没有任何改进,我也测试了我的应用程序。
    我的应用程序运行流畅和稳定的唯一情况是在我的手机上的 Android 5.0 Lollipop API 21 上。我可以一个接一个地断开所有客户端,我可以停用连接客户端的服务器,即使启用了 UI 功能,一切正常。 也许在 Jelly Bean 或 Delphi 等旧版本的 Android 上需要做一些系统配置?很遗憾我的平板电脑无法升级到 API 5.0。

    确切地说,我将向您展示 UI 功能: (我正在制作自己的 ClientsList,因为我必须记住更多数据,例如设备名称或序列号。使用我自己的代码更容易。)

    <code>
    
    //  -----------------------------------------------------------------  LIST VIEW
    procedure THeaderFooterwithNavigation.LV_MakeLine;
    var
      Item  :  TListItem;
    begin
        Item := LV.Items.Add;
    end;
    
    
    procedure THeaderFooterwithNavigation.LV_AddData (index : Word);
    var
      Item    : TListItem;
      Client  : TClientTcp;
      ip      : String;
      port    : String;
      name    : String;
    begin
        Client  := ClientsList.Items [index];
        Item    := LV.Items.Item [index];
        LV.Items.Item [index].Text := Client.Name + ' ' + Client.IP + ' : ' + IntToStr(Client.Port);
    end;
    
    
    procedure  THeaderFooterwithNavigation.LV_Refresh;
    var
        i     : Integer;
        itms  : Integer;
    begin
        LV.Items.Clear;
        //LV.ClearItems;
    
        itms := ClientsList.Count;
        for  i := 0  to  itms-1  do begin
            LV_MakeLine ();
            LV_AddData (i);
        end;
    end;
    
    
    procedure THeaderFooterwithNavigation.CL_DeleteClient (ip : String; port : Word);
    var
        cl_item   :   Integer;
    begin
        cl_item := ClientsList.FindClient_ByIpPort (ip, port);
        if  cl_item  >  (-1)  then begin
            //  DELETE DISCONNECTED CLIENT FROM LIST AND SET LIST SIZE TO THE CLIENTS NUMBER
            ClientsList.Delete (cl_item);
        end;
    end;
    

    和客户名单

    单位 ServerTcpA;
    
    界面
    {
    用途
      SysUtils、变体、类、Generics.Collections;
    }
    
    
    用途
      System.SysUtils,System.Types,System.UITypes,System.Classes,System.Variants,
      System.Generics.Collections;
    
    
    {
    用途
      Windows、消息、SysUtils、变体、类、图形、控件、表单、
      对话框,ExtCtrls,StdCtrls,Contnrs,ComCtrls,
      按钮、WinSock、ScktComp;
    }
    
    类型
        // 类声明 ---------------------------------------------- ------
    
    
        // TClientTcp 类 ---------------------------------------------- --------
        TClientTcp = 类(TObject)
            上市
                名称:字符串;
                IP:字符串;
                端口:整数;
                RecFrames:整数;
        结尾;
    
    
    
        // TClientsTcpList 类 ---------------------------------- ---
        TClientsTcpList = 类(TObjectList )
            私人的
                函数 FGetItem(索引:整数):TClientTcp;
            上市
                属性 Items [index : Integer] : TClientTcp 读取 FGetItem;
                函数添加(名称:字符串;ip:字符串;端口:整数):TClientTcp;
                函数 FindClient_ByName (name : String) : TClientTcp;
                函数 FindClient_ByIp (ip : String) : TClientTcp;
                函数 FindClient_ByPort(端口:整数):整数;
                函数 FindClient_ByIpPort(ip:字符串;端口:整数):整数;
        结尾;
    
    
    
        // TTcpCfg 类 ---------------------------------------------- ------------
        TTcpCfg = 类(TObject)
            TcpClientsList:TClientsTcpList;
    
            上市
                构造函数创建;
                析构函数销毁;覆盖;
                函数 AddClient(ip:字符串;端口:整数):TClientTcp;
        结尾;
    
    
    
        // ENUM - 定义的列名 -------------------------------
        键入 TColNames = (
            COL_LP = 0,
            COL_NAME,
            COL_IP,
            COL_PORT
        );
    
    
    
    常量
        无 = -1;
    
    
    
    变量
        //ServerTcpDK : TServerSocket;
        //TCPCFG : TTcpCfg;
        客户端列表:TClientsTcpList;
    
    
    执行
    
    
    
    
    
    // ================================================= === 类:TClientsTcpList
    
    // 获取项目
    函数 TClientsTcpList.FGetItem(索引:整数):TClientTcp;
    开始
        //Result := 继承 GetItem (index) as TClientTcp;
        结果 := 作为 TClientTcp 继承的 Items [index];
    结尾;
    
    
    //  添加项目
    函数 TClientsTcpList.Add(名称:字符串;ip:字符串;端口:整数):TClientTcp;
    开始
        if (FindClient_ByIpPort (ip, port) = NONE) 然后开始
            结果:= TClientTcp.Create;
            结果.名称:=名称;
            结果.IP := ip;
            结果.端口:=端口;
            结果.RecFrames := 0;
    
            继承添加(结果);
        结尾;
    结尾;
    
    
    
    // 查找客户:按名称
    函数 TClientsTcpList.FindClient_ByName(名称:字符串):TClientTcp;
    变量
      我:整数;
    开始
        //结果 := nil;
        结果:=零;
    
        for i:=0 to Count-1 do begin
            如果 Items [i].Name = name 然后开始
                结果:=项目[i];
                休息;
            结尾;
        结尾;
    结尾;
    
    
    
    // 查找客户:通过 IP
    函数 TClientsTcpList.FindClient_ByIp (ip : String): TClientTcp;
    变量
      我:整数;
    开始
        //结果 := nil;
        结果:=零;
    
        for i:=0 to Count-1 do begin
            如果 Items [i].IP = ip 然后开始
                结果:=项目[i];
                休息;
            结尾;
        结尾;
    结尾;
    
    
    
    // 查找客户:按端口 ------------------------------------------- ------------
    // @Ret: LIST 中的项目索引
    // -1: 未找到
    函数 TClientsTcpList.FindClient_ByPort(端口:整数):整数;
    变量
      我:整数;
    开始
        结果:=无;
    
        for i:=0 to Count-1 do begin
            如果 Items [i].Port = port 然后开始
                结果 := i;
                休息;
            结尾;
        结尾;
    结尾;
    
    
    
    // 查找客户:按 IP 和端口 ----------------------------------------- ------
    // @Ret: LIST 中的项目索引
    // -1: 未找到
    函数 TClientsTcpList.FindClient_ByIpPort(ip:字符串;端口:整数):整数;
    变量
      我:整数;
    开始
        结果:=无;
    
        for i:=0 to Count-1 do begin
            if (Items [i].IP = ip) 和 (Items [i].Port = port) 然后开始
                结果 := i;
                休息;
            结尾;
        结尾;
    结尾;
    
    
    
    // ================================================= =========== 类:TTcpCfg
    构造函数 TTcpCfg.Create;
    开始
        遗传;
        TcpClientsList := TClientsTcpList.Create;
    结尾;
    
    
    
    析构函数 TTcpCfg.Destroy;
    开始
        TcpClientsList.Free;
        遗传;
    结尾;
    
    
    
    函数 TTcpCfg.AddClient(ip:字符串;端口:整数):TClientTcp;
    开始
        结果:= TClientTcp.Create;
        //TcpClientsList.Add (Result);
    
        结果.IP := ip;
        结果.端口:=端口;
        结果.RecFrames := 0;
    结尾;
    
    
    
    // ================================================= ============ 初始化
    初始化
        //ServerTcpDK := TServerSocket.Create (Nil);
        //TCPCFG := TTcpCfg.Create;
        ClientsList := TClientsTcpList.Create;
    
    
    定稿
        //ServerTcpDK.Free;
        //TCPCFG.Free;
        客户名单。免费;
    
    
    // @END OF 文件 -------------------------------------------- ------------------
    结尾。
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多