2014-05-25 93 views
0

如何在Indy/Lazarus上正确关闭TIdTCPServer,如果我们在GUI中按下“关闭”按钮?感谢您的帮助! (更改我的原始问题)Indy/lazarus TIdTCPServer如何正确关闭

如何在客户端断开连接时关闭TIdTCPServer?

应该例外处理什么?

IO的工作原理,但它有点不稳定。 这里是代码如下:

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. 

回答

1

此代码有很多错误。滥用Contexts列表。不当使用BytesToRaw()RawToBytes()。工作线程中线程不安全的GUI逻辑。此代码非常容易出现内存损坏和死锁。难怪你的代码不稳定。你需要解决这个问题。

为了回答您的具体问题:

如何与印/拉撒路正常关闭一TIdTCPServer如果我们在GUI中按下“关闭”按钮?

只需停用/销毁服务器。它会自动关闭任何活动的客户端连接。但是,由于TIdTCPServer的多线程性质,您必须确保在停用期间不阻止任何服务器的事件处理程序,否则会导致代码死锁。如果事件处理程序必须在主线程停用服务器时与主线程同步,请使用异步同步(TThread.Queue(),TIdNotify等)或在工作线程中停用,以免主线程阻塞。另外,如果您需要在事件处理程序中捕获异常,请务必重新提出您捕获的任何EIdException衍生的异常,并让服务器处理它,否则客户端线程将无法正确终止,也会导致死锁失效。

如何在客户端断开连接时关闭TIdTCPServer?

服务器无法从其自己的事件(死锁)内部停用。您将不得不异步执行停用。在OnDisconnect事件中,您可以向自己发送异步信号,以便事件处理程序可以退出,然后在处理信号时停用服务器。或者产生一个工作线程来执行去激活。