2016-11-02 82 views
3

TIdTCPServer에 연결된 비활성 클라이언트를 인터넷에서 연결 해제했는지 또는 비활성 시간 동안 연결을 끊으려고합니다.TIdTCPServer로 비활성 클라이언트를 어떻게 연결 해제합니까?

procedure TservForm.TcpServerConnect(AContext: TIdContext); 
begin 
    AContext.Connection.IOHandler.ReadTimeout := 26000; 
    AContext.Binding.SetSockOpt(Id_SOL_SOCKET, Id_SO_SNDTIMEO, 15000); 
end; 

을하지만 클라이언트 연결이 끊어진 후 분리가 트리거되지 않는 것 같다

나는 다음과 같은 OnConnect 이벤트에 시간 제한을 설정했습니다.

SetKeepAliveValues()을 사용해 보았지만 비활성 클라이언트를 연결 해제하는 데 너무 많은 시간이 걸립니다.

비활성 클라이언트를 연결 해제하는 데 유용한 방법이 있습니까? 따라서 클라이언트가 30 초 내에 아무 것도 보내지 않거나 보내지 않으면 서버에서 연결을 끊습니다.

이벤트를

procedure TservForm.TcpServerExecute(AContext: TIdContext); 
var 
    Connection: TConnection; 
    cmd: String; 
    Cache, OutboundCmds: TStringList; 
    MS: TMemoryStream; 
    I: integer; 
    S: String; 
begin 
    Connection := AContext as TConnection; 

    // check for pending outbound commands... 
    OutboundCmds := nil; 
    try 
    Cache := Connection.OutboundCache.Lock; 
    try 
     if Cache.Count > 0 then 
     begin 
     OutboundCmds := TStringList.Create; 
     OutboundCmds.Assign(Cache); 
     Cache.Clear; 
     end; 
    finally 
     Connection.OutboundCache.Unlock; 
    end; 

    if OutboundCmds <> nil then 
    begin 
     for I := 0 to OutboundCmds.Count - 1 do 
     begin 
     AContext.Connection.IOHandler.Writeln(OutboundCmds.Strings[I], 
      IndyTextEncoding_UTF8); 
     MS := TMemoryStream(OutboundCmds.Objects[I]); 
     if MS <> nil then 
     begin 
      AContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8; 
      AContext.Connection.IOHandler.LargeStream := true; 
      AContext.Connection.IOHandler.Write(MS, 0, true); 
     end; 
     end; 
    end; 
    finally 
    if OutboundCmds <> nil then 
    begin 
     for I := 0 to OutboundCmds.Count - 1 do 
     OutboundCmds.Objects[I].Free; 
    end; 
    OutboundCmds.Free; 
    end; 

    // check for a pending inbound command... 
    if AContext.Connection.IOHandler.InputBufferIsEmpty then 
    begin 
    AContext.Connection.IOHandler.CheckForDataOnSource(100); 
    AContext.Connection.IOHandler.CheckForDisconnect; 
    if AContext.Connection.IOHandler.InputBufferIsEmpty then 
    begin 
    Exit; 
    end; 
    end; 

    cmd := AContext.Connection.Socket.ReadLn(IndyTextEncoding_UTF8); 

    ............... 
    ............... 
+0

무엇 당신의'OnExecute' 이벤트 핸들러가 생겼는데? 사용중인 제한 시간은 수행중인 I/O 종류에 따라 다릅니다. 또한,'SetKeepAliveValues ​​()'는 모든 플랫폼이 keepalive timeout 간격을 커스터마이징하는 것은 아니지만, 죽은 연결 (비활성 연결은 아님)을 감지하기 위해 작동합니다. 또 다른 옵션은 비활성 시간 제한이 경과 한 후에 연결을 모니터하고 소켓을 닫는 작업자 스레드를 실행하는 것입니다. TIdContext.Data' 속성을 사용하여 마지막으로 보내거나받은 시간을 추적 할 수 있습니다. –

+0

@RemyLebeau가 클라이언트 측에서 실행될 때마다 추가됩니다. 서버에 명령을 보내려면 타이머를 각각 10 초로 설정하십시오. –

답변

2

ReadLn()가 유휴 시간 동안 도달하지 않기 때문에 클라이언트가 분리하지 않기 때문에 ReadTimeout 효과가 있고,하지 않는 실행에 당신은 그 소켓을 명령을 많이 보내는하지 않는 경우 버퍼가 채워지지 않으므로 SO_SNDTIMEO도 영향을 미치지 않습니다. 이미 일부 수동 타임 아웃 처리를하고 있기 때문에

, 당신은뿐만 아니라 유휴 타임 아웃을 처리하기 위해 그것을 확장 할 수 있습니다, 예를 들면 :

type 
    TConnection = class(TIdServerContext) 
    ... 
    public 
    LastSendRecv: LongWord; 
    ... 
    end; 

... 

procedure TservForm.TcpServerConnect(AContext: TIdContext); 
var 
    Connection: TConnection; 
begin 
    Connection := AContext as TConnection; 
    AContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8; 
    AContext.Connection.IOHandler.LargeStream := True; 
    AContext.Connection.IOHandler.ReadTimeout := 30000; 
    AContext.Binding.SetSockOpt(Id_SOL_SOCKET, Id_SO_SNDTIMEO, 15000); 
    Connection.LastSendRecv := Ticks; 
end; 

procedure TservForm.TcpServerExecute(AContext: TIdContext); 
var 
    Connection: TConnection; 
    cmd: String; 
    Cache, OutboundCmds: TStringList; 
    MS: TMemoryStream; 
    I: integer; 
    S: String; 
begin 
    Connection := AContext as TConnection; 

    // check for pending outbound commands... 
    OutboundCmds := nil; 
    try 
    Cache := Connection.OutboundCache.Lock; 
    try 
     if Cache.Count > 0 then 
     begin 
     OutboundCmds := TStringList.Create; 
     OutboundCmds.Assign(Cache); 
     Cache.Clear; 
     end; 
    finally 
     Connection.OutboundCache.Unlock; 
    end; 

    if OutboundCmds <> nil then 
    begin 
     for I := 0 to OutboundCmds.Count - 1 do 
     begin 
     AContext.Connection.IOHandler.WriteLn(OutboundCmds.Strings[I]); 
     MS := TMemoryStream(OutboundCmds.Objects[I]); 
     if MS <> nil then    
      AContext.Connection.IOHandler.Write(MS, 0, true);  
     end; 
     Connection.LastSendRecv := Ticks; 
    end; 
    finally 
    if OutboundCmds <> nil then 
    begin 
     for I := 0 to OutboundCmds.Count - 1 do 
     OutboundCmds.Objects[I].Free; 
    end; 
    OutboundCmds.Free; 
    end; 

    // check for a pending inbound command... 
    if AContext.Connection.IOHandler.InputBufferIsEmpty then 
    begin 
    AContext.Connection.IOHandler.CheckForDataOnSource(100); 
    AContext.Connection.IOHandler.CheckForDisconnect; 
    if AContext.Connection.IOHandler.InputBufferIsEmpty then 
    begin 
     if GetTickDiff(Connection.LastSendRecv, Ticks) >= 30000 then 
     AContext.Connection.Disconnect; 
     Exit; 
    end; 
    end; 

    cmd := AContext.Connection.Socket.ReadLn;  
    Connection.LastSendRecv := Ticks; 

    ... 
end; 
+0

어디에서 TIdTicks가 선언 되었습니까? –

+0

'IdGlobal' 단위에서. –

+0

음, idglobal을 uses에 추가했으나 선언하지 않았습니다. –