title: Delphi Android App INDY TCP Server - Application closes after client disconnect
body: <p>I need help very much.</p>
  
  <p>I have a TCP server application based on Indy 10's <code>TIdTCPServer</code> component, which I want to run on Win32 and Android. I'm using Delphi XE7.</p>
  
  <p>The server has to handle 10 clients.</p>
  
  <ul>
  <li><p>Application works fine on Windows and Android on: CONNECTING, SENDING, RECEIVING data, but there is a problem with the <code>OnDisconnect</code> event on Android only. The application works totally fine on Windows, but on Android there is a big problem with disconnecting the clients and on event: <code>TCPServer.Active := FALSE</code>. On 90% of cases the application is being automatically closed when I disconnect a client.</p></li>
  <li><p>When I start the server: <code>TCPServer1.Active := TRUE</code>, and then I close it <code>TCPServer1.Active := FALSE</code>, without connecting the clients, the application works fine.</p></li>
  </ul>
  
  <p>I'm adding my code under. I have used hints from Remy Lebeau.</p>
  
  <ul>
  <li>I'm testing the app with two clients.</li>
  <li>I'm showing connected clients in the ListView. </li>
  <li>I'm updating the ListView not from the Server Events, but in the Timer event.</li>
  <li>App has 3 buttons: Server Listen, Server Close, Send (clients number selected in the ListView)</li>
  </ul>
  
  <p>Please help.</p>
  
  <pre><code>//  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) &lt;&gt; wrSignaled then Exit;
    List := FQueue.Lock;
    try
      if List.Count &gt; 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 &lt;TIdContext&gt;;
    {$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 &lt;&gt; 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 &lt;&gt; 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 &lt;TIdContext&gt;;
    {$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 &gt; 200)  and  (port &lt; 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  &gt;  -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 &lt;TIdContext&gt;;
    {$ENDIF Android}
  begin
    try
      clist := TCPServer1.Contexts.LockList();
      Clients_Num := TCPServer1.Contexts.Count;
    finally
      TCPServer1.Contexts.UnlockList;
    end;
  end;
  </code></pre>