2014-05-25 1 views
0

GUI에서 '닫기'버튼을 누르면 Indy/Lazarus로 TIdTCPServer를 올바르게 닫는 방법? 도와 줘서 고맙다! (원래 질문 변경)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 논리. 이 코드는 메모리 손상과 교착 상태에 매우 취약합니다. 코드가 불안정한 것은 당연합니다. 문제를 해결해야합니다.

는 특정 질문에 대답하려면, 우리는 GUI에서 '닫기'버튼을 누르면

어떻게 인디/나사로 제대로 TIdTCPServer을 닫습니다?

서버를 단순히 비활성화/제거하십시오. 활성 클라이언트 연결을 자동으로 닫습니다. 그러나 TIdTCPServer의 멀티 스레드 특성으로 인해 비활성화 중에 서버의 이벤트 처리기를 차단하지 않도록해야합니다. 그렇지 않으면 코드가 교착 상태가됩니다. 주 스레드가 서버를 비활성화하는 동안 이벤트 처리기가 주 스레드와 동기화되어야하는 경우 비동기 동기화 (TThread.Queue(), TIdNotify 등)를 사용하거나 작업 스레드에서 비활성화하여 주 스레드가 차단되지 않도록하십시오. 또한 이벤트 처리기에서 예외를 catch해야하는 경우 catch하고 서버에서 처리하도록 허용 한 EIdException 파생 예외를 다시 발생 시키십시오. 그렇지 않으면 클라이언트 스레드가 올바르게 종료되지 않고 교착 비활성화가 발생합니다.

어떻게 클라이언트 연결을 끊을 경우 TIdTCPServer을 닫습니다?

서버

는 자체 이벤트 (교착 상태) 내부에서 비활성화 할 수 없습니다. 비동기 적으로 비활성화를 수행해야합니다. OnDisconnect 이벤트에서 비동기 신호를 보내 이벤트 처리기를 종료 한 다음 신호가 처리 될 때 서버를 비활성화 할 수 있습니다. 또는 비활성화를 수행 할 작업자 스레드를 생성 할 수 있습니다.