简体   繁体   English

Indy / Lazarus TIdTCPServer如何正确关闭

[英]Indy/lazarus TIdTCPServer how to close properly

How to close properly a TIdTCPServer with Indy/Lazarus if we press a 'Close' button in the GUI?. 如果我们在GUI中按下“关闭”按钮,如何用Indy / Lazarus正确关闭TIdTCPServer? Thanks for helping! 感谢您的帮助! (Changed my original question) (改变了我原来的问题)

How to close the TIdTCPServer if a client disconnects? 如果客户端断开连接,如何关闭TIdTCPServer?

Should the exception handle anything? 异常应该处理什么吗?

The IO works but it's a bit unstable yet. IO可以工作,但是还不稳定。 Here is the code below: 这是下面的代码:

unit pt_socket;

{$mode objfpc}//{$H+}

interface

uses
  Classes, SysUtils,
  Forms,
  IdGlobal,
  IdContext, IdComponent,
  IdTCPServer, IdTCPClient,
  Controls, Graphics, Dialogs;

type
  TSocket = class
  private
    IdTcpServer1: TIdTCPServer;
    IdTcpClient1: TIdTCPClient;
    procedure IdTCPServer1Connect(AContext: TIdContext);
    procedure IdTCPServer1Disconnect(AContext: TIdContext);
    //procedure IdTCPServer1Exception(AContext: TIdContext; AException: Exception);
    procedure IdTCPServer1Execute(AContext: TIdContext);
  public
    procedure init;
    function Open: boolean;
    procedure Close;
    function Write(str: TByteArray; len: integer): integer;
  end;

var
  lst: Tlist;

implementation

uses main, pt_settings, pt_ctlpanel, pt_terminal;

procedure TSocket.init;
begin
end;

procedure TSocket.IdTCPServer1Connect(AContext: TIdContext);
begin
  MainApp.GuiPortOpen;
  lst := IdTcpServer1.Contexts.LockList;
end;

procedure TSocket.IdTCPServer1Disconnect(AContext: TIdContext);
begin
  IdTcpServer1.Contexts.UnlockList;
  MainApp.GuiPortClose;
end;

//procedure TSocket.IdTCPServer1Exception(AContext: TIdContext; AException: Exception);
//begin
//    MainApp.GuiPortClose;
//end;

procedure TSocket.IdTCPServer1Execute(AContext: TIdContext);
var
  Socket_Receive_Buffer: TIdBytes;
  Socket_Input_Length: integer;
  Input_Buffer: TByteArray;

begin
  with AContext.Connection do
  begin
    IOHandler.ReadBytes(Socket_Receive_Buffer, -1, false);
    Socket_Input_Length := Length(Socket_Receive_Buffer);
    if Socket_Input_Length > 0 then
    begin
      BytesToRaw(Socket_Receive_Buffer,Input_Buffer,Socket_Input_Length);
      Terminal.GuiTerminalPutInput(Input_Buffer, Socket_Input_Length);
    end;
  end;
end;

function TSocket.Open: boolean;
begin
  if Settings.SocketModeRadioGroup.ItemIndex = 0 then
  begin
    IdTcpServer1 := TIdTCPServer.Create(nil);
    IdTCPServer1.OnExecute := @IdTCPServer1Execute;
    IdTCPServer1.OnConnect := @IdTCPServer1Connect;
    IdTCPServer1.OnDisconnect := @IdTCPServer1Disconnect;
    //IdTcpServer1.OnException := @IdTCPServer1Exception;
    IdTcpServer1.DefaultPort := StrToInt(Settings.SocketPortEdit.Text);
    IdTcpServer1.MaxConnections := 1;
    IdTCPServer1.Bindings.Add.IPVersion := Id_IPv4;
    IdTcpServer1.Active := True;
  end
  else
  begin
    IdTcpClient1 := TIdTCPClient.Create(nil);
    //IdTcpClient1.DefaultPort := StrToInt(Settings.SocketPortEdit.SelText);
  end;
end;

procedure TSocket.Close;
begin
  if Settings.SocketModeRadioGroup.ItemIndex = 0 then
  begin
    IdTcpServer1.Destroy;
  end
  else
  begin
    IdTcpClient1.Destroy;
  end;
end;

function TSocket.Write(str: TByteArray; len: integer): integer;
var
  Socket_Transmit_Buffer: TIdBytes;

begin
  Socket_Transmit_Buffer := RawToBytes(str,len);
  if len > 0 then

  // Only one connection by design
  with TIdContext(lst.Items[0]).Connection do
  begin
    IOHandler.Write(Socket_Transmit_Buffer);
  end;
  Result := len;
end;

end.

This code has tons of mistakes in it. 这段代码有很多错误。 Misuse of the Contexts list. 滥用Contexts列表。 Improper use of BytesToRaw() and RawToBytes() . BytesToRaw()RawToBytes()使用不当。 Thread-unsafe GUI logic in worker threads. 工作线程中的线程不安全GUI逻辑。 This code is very prone to memory corruption and deadlocks. 此代码非常容易导致内存损坏和死锁。 It is no wonder your code is unstable. 难怪您的代码不稳定。 You need to fix that. 您需要解决此问题。

To answer your specific questions: 要回答您的特定问题:

How to close properly a TIdTCPServer with Indy/Lazarus if we press a 'Close' button in the GUI?. 如果我们在GUI中按下“关闭”按钮,如何用Indy / Lazarus正确关闭TIdTCPServer?

Simply deactivate/destroy the server. 只需停用/销毁服务器即可。 It will automatically close any active client connection. 它将自动关闭任何活动的客户端连接。 However, due to the multi-threaded nature of TIdTCPServer you have to make sure NOT to block any of the server's event handlers during deactivation, or you will deadlock your code. 但是,由于TIdTCPServer的多线程性质,您必须确保在停用期间不要阻塞服务器的任何事件处理程序,否则将使代码死锁。 If the event handlers have to sync with the main thread while the main thread is deactivating the server, use asynchronous syncs ( TThread.Queue() , TIdNotify , etc) or do the deactivation in a worker thread so the main thread is not blocked. 如果在主线程停用服务器时事件处理程序必须与主线程同步,请使用异步同步( TThread.Queue()TIdNotify等)或在工作线程中停用,以便不阻塞主线程。 Also, if you need to catch exceptions in your event handlers, be sure to re-raise any EIdException -derived exception you catch and let the server handle it, otherwise the client threads will not terminate correctly, deadlocking deactivation as well. 另外,如果您需要在事件处理程序中捕获异常,请确保重新引发捕获的任何EIdException派生的异常,并让服务器处理它,否则客户端线程将无法正确终止,也将导致死锁。

How to close the TIdTCPServer if a client disconnects? 如果客户端断开连接,如何关闭TIdTCPServer?

The server cannot be deactivated from inside its own events (deadlock). 无法从其自身的事件(死锁)内部停用服务器。 You will have to perform the deactivation asynchronously. 您将必须异步执行停用操作。 In the OnDisconnect event, you can send yourself an asynchronous signal so the event handler can exit, and then deactivate the server when the signal is processed. OnDisconnect事件中,您可以向自己发送异步信号,以便事件处理程序可以退出,然后在处理信号时停用服务器。 Or spawn a worker thread to perform the deactivation. 或生成一个工作线程来执行停用操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM