Q:

the Difference of TIdTCPConnection.Disconnect and TIdIOHandler.Close

I use Indy 10.6.2.5298.
What is the difference of TIdTCPConnection.Disconnect and TIdIOHandler.Close? Both of them disconnect the line but sometimes the former makes an access violation error.
I am sorry that I can't understand it through the help documents and their source codes.
type
  TForm1 = class(TForm)
    IdTCPServer1: TIdTCPServer;
    procedure FormClick(Sender: TObject);
    procedure IdTCPServer1Execute(AContext: TIdContext);
  private
    TestContext: TIdContext;
  end;

procedure TForm1.FormClick(Sender: TObject);
begin
  TestContext.Connection.Disconnect; // access violation

  TestContext.Connection.IOHandler.Close; // always works well
end;

procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
begin
  TestContext := AContext;

  AContext.Connection.Disconnect; // works well
end;

A:

TIdTCPConnection.Disconnect() calls IOHandler.Close() internally, if an IOHandler is assigned and has been opened (it also calls TIdTCPConnection.DisconnectNotifyPeer() and triggers the OnDisconnected and OnStatus events):
procedure TIdTCPConnection.Disconnect(ANotifyPeer: Boolean);
var
  // under ARC, convert a weak reference to a strong reference before working with it
  LIOHandler: TIdIOHandler;
begin
  try
    // Separately to avoid calling .Connected unless needed
    if ANotifyPeer then begin
      // TODO: do not call Connected() here if DisconnectNotifyPeer() is not
      // overriden. Ideally, Connected() should be called by overridden
      // DisconnectNotifyPeer() implementations if they really need it. But
      // to avoid any breakages in third-party overrides, we could check here
      // if DisconnectNotifyPeer() has been overridden and then call Connected()
      // to maintain existing behavior...
      //
      try
        if Connected then begin
          DisconnectNotifyPeer;
        end;
      except
        // TODO: maybe allow only EIdConnClosedGracefully and EIdSocketError?
      end;
    end;
  finally
    {
     there are a few possible situations here:
     1) we are still connected, then everything works as before,
        status disconnecting, then disconnect, status disconnected
     2) we are not connected, and this is just some "rogue" call to
        disconnect(), then nothing happens
     3) we are not connected, because ClosedGracefully, then
        LConnected will be false, but the implicit call to
        CheckForDisconnect (inside Connected) will call the events
    }
    // We dont check connected here - we realy dont care about actual socket state
    // Here we just want to close the actual IOHandler. It is very possible for a
    // socket to be disconnected but the IOHandler still open. In this case we only
    // care of the IOHandler is still open.
    //
    // This is especially important if the socket has been disconnected with error, at this
    // point we just want to ignore it and checking .Connected would trigger this. We
    // just want to close. For some reason NS 7.1 (And only 7.1, not 7.0 or Mozilla) cause
    // CONNABORTED. So its extra important we just disconnect without checking socket state.
    LIOHandler := IOHandler;
    if Assigned(LIOHandler) then begin
      if LIOHandler.Opened then begin
        DoStatus(hsDisconnecting);
        LIOHandler.Close;
        DoOnDisconnected;
        DoStatus(hsDisconnected);
        //LIOHandler.InputBuffer.Clear;
      end;
    end;
  end;
end;

TIdIOHandler.Close() simply closes the socket, if one has been allocated:
procedure TIdIOHandlerSocket.Close;
begin
  if FBinding <> nil then begin
    FBinding.CloseSocket;
  end;
  inherited Close;
end;

procedure TIdIOHandler.Close;
//do not do FInputBuffer.Clear; here.
//it breaks reading when remote connection does a disconnect
var
  // under ARC, convert a weak reference to a strong reference before working with it
  LIntercept: TIdConnectionIntercept;
begin
  try
    LIntercept := Intercept;
    if LIntercept <> nil then begin
      LIntercept.Disconnect;
    end;
  finally
    FOpened := False;
    WriteBufferClear;
  end;
end;

The reason for your access violation error is likely because your test code is not thread-safe to begin with.  TIdTCPServer is a multi-threaded component.  Its OnConnect, OnDisconnect, OnExecute, and OnException events are triggered in the context of a worker thread that manages the TIdContext object.  Your OnClick handler is accessing the TIdContext object outside of that thread.  As soon as the socket is closed, TIdTCPServer will detect that and stop the thread, destroying the TIdContext and its TIdTCPConnection and TIdIOHandler objects.  Due to thread timing and context switching, your OnClick handler may very well be continuing to access those objects after they have been destroyed.  You don't have that problem inside of the OnExecute handler because the objects are still valid while the thread is running.
To make your OnClick code play nice with TIdTCPServer, you need to lock the TIdTCPServer.Contexts list so the TIdContext object cannot be destroyed while OnClick is still trying to use it, eg:
procedure TForm1.FormClick(Sender: TObject);
var
  List: TIdContextList;
begin
  List := IdTCPServer1.Contexts.LockList;
  try
    //has the context already been removed?
    if List.IndexOf(TestContext) <> -1 then
      TestContext.Connection.Disconnect;
  finally
    IdTCPServer1.Contexts.UnlockList;
  end;
end;

