delphi - 如何使用 TIdTCPServer 断开不活动客户端的连接?

标签 delphi indy indy10 delphi-xe8

我正在尝试断开连接到 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;

关于delphi - 如何使用 TIdTCPServer 断开不活动客户端的连接?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40374396/

相关文章:

德尔福印地 Ping 错误 10040

delphi - Indy:TidMessage 删除所有重音,如何禁用它?

delphi - 如何解析RTSP请求和响应头?

algorithm - 对包含 7 个整数的数组进行排序的最快方法是什么?

delphi - 使用 TwebBrowser 可以正常登录站点,但使用 TidHTTP 则不行

Delphi HMAC SHA512 签署了对 Bittrex 交易所的调用

delphi - 如何将 DataSnap 客户端连接定向到各种 DS Server?

delphi - 数据库间交易监控

mysql - Delphi 从 MySql 加载 Blob

delphi - 如何在具有透明度和不透明度的 Canvas 上绘画?