2016-11-02 5 views
3

Я пытаюсь отключить неактивные клиенты, подключенные к TIdTCPServer, независимо от того, отключены ли эти клиенты от их Интернета или в течение неактивного времени.Как отключить неактивные клиенты с помощью TIdTCPServer?

Я попытался установить время ожидания в OnConnect события, как следующее:

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

Но, кажется, разъединение не запускается после того, как соединение клиента теряется.

Я попытался использовать 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' обработчик событий выглядит? Тайм-ауты, которые вы используете, зависят от типа ввода-вывода, который вы выполняете. Кроме того, 'SetKeepAliveValues ​​()' работает для обнаружения мертвых соединений (но не неактивных подключений), хотя не все платформы поддерживают настройку интервалов таймаута keepalive. Другим вариантом будет запуск рабочего потока, который отслеживает соединения и закрывает их сокеты после истечения неактивных тайм-аутов. Вы можете использовать свойство 'TIdContext.Data' для отслеживания последнего времени отправки/получения. –

+0

@RemyLebeau on execute добавлено, на стороне клиента я устанавливаю таймер для отправки команды на сервер каждые 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

hmm Я добавил idglobal в использование, но его необъявленный –

Смежные вопросы