簡體   English   中英

如何使用TIdTCPServer斷開非活動客戶端?

[英]how do I disconnect inactive clients with TIdTCPServer?

我正在嘗試斷開連接到TIdTCPServer非活動客戶端,無論這些客戶端是否與Internet斷開連接,或者是否處於非活動狀態。

我嘗試在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);

  ...............
  ...............

客戶端沒有斷開,因為在空閑時間沒有達到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;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM