【问题标题】:TIdTCPServer disconnect a client totally from the serverTIdTCPServer 完全断开客户端与服务器的连接
【发布时间】:2017-07-09 21:16:57
【问题描述】:

我正在使用TIdTCPServer。我有一个客户端异常断开连接。 我正在尝试断开此客户端的连接,如下所示:

//class TClientConnection = class(TIdServerContext)

var
  Clienttodisconnect: TClientConnection;

List := Server.Contexts.LockList;
try
  for I := 0 to List.Count - 1 do
  begin
    Clienttodisconnect := TClientConnection(List.Items[I]);
    if Clienttodisconnect.uuid = idtodiscnnect then
    begin
      try    
        Clienttodisconnect.Connection.Disconnect;    
      except
      end;
    end;
  end;
finally
  Server.Contexts.UnlockList;
end;

客户端有时会与服务器断开连接,有时会卡住,直到服务器重新启动。

我做错了什么?我只想断开客户端与上下文的连接。

这里是服务器 onexecute 事件

var
  Connection: TClientConnection;
  CMD: String;
  Cache, OutboundCmds: TStringList;
  I: integer;
  UConnected : Boolean;
  Len: Integer;
begin

sleep(10);

Try
UConnected := AContext.Connection.Connected;
Except
UConnected := False;
End;

If UConnected <> True Then
begin
AContext.Connection.Disconnect;
exit;
end;

Len := AContext.Connection.IOHandler.InputBuffer.Size;


If Len >= 200000 then
begin
AContext.Connection.Disconnect;
exit;

end;

Connection := AContext as TClientConnection;



  // 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);
      end;
      Connection.LastSendRecv := Ticks64;
    end;




  finally
    if OutboundCmds <> nil then
    begin
      for I := 0 to OutboundCmds.Count - 1 do
      begin
        OutboundCmds.Objects[I].Free;
      end;
    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 GetElapsedTicks(Connection.LastSendRecv) >= 30000 then
     AContext.Connection.Disconnect;
     Exit;
    end;
  end;

.......

........ 

【问题讨论】:

  • idtodiscnnect 来自哪里?其余的(除了当你找到正确的客户时异常吃和没有退出循环)似乎很好。
  • 不要让 except 块裸露!始终记录错误(或在对话框或其他地方显示),它可以为您的调试过程提供重要信息
  • @Victoria idtodiconnect 是从客户端发送到服务器的 const 参数。
  • 我在异常块上进行了一些日志跟踪,并且在断开异常断开的客户端时没有引发任何异常。
  • 您的OnExecute 处理程序是什么样的?它的编码方式对TIdTCPServer 进程如何断开连接有很大影响。而且这个问题中显示的代码无论如何都不是断开客户端的最安全方法。最好在 OnExecute 事件中处理它。使用超时,或标记TClientConnection 并让OnExecute 查找该标记,因此可以在拥有客户端连接的线程中调用Disconnect()

标签: delphi indy indy10


【解决方案1】:

我会建议更像这样的东西:

type
  TClientConnection = class(TIdServerContext)
  public
    Cache: TIdThreadSafeStringList;
    uuid: string; // or TGuid or whatever you are using...
    ForceDisconnect: Boolean; // <-- add this
  end;

...

var
  List: TList; // or TIdContextList in modern Indy versions
  I: Integer;
  Client: TClientConnection;
begin
  List := Server.Contexts.LockList;
  try
    for I := 0 to List.Count - 1 do
    begin
      Client := TClientConnection(TIdContext(List.Items[I]));
      if Client.uuid = idtodiscnnect then
      begin
        Client.ForceDisconnect := True; // <-- don't actually disconnect here, just signal it
        Break;
      end;
    end;
  finally
    Server.Contexts.UnlockList;
  end;
end;

...

procedure TMyForm.ServerConnect(AContext: TIdContext);
begin
  (AContext as TClientConnection).LastSendRecv := Ticks64;
  AContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
  AContext.Connection.IOHandler.ReadTimeout := 30000;
end;

procedure TMyForm.ServerExecute(AContext: TIdContext);
var
  Client: TClientConnection;
  CMD: String;
  Cache, OutboundCmds: TStringList;
  I: integer;
  Len: Integer;
begin
  Client := AContext as TClientConnection;

  if Client.ForceDisconnect then // <-- do the actual disconnect here
  begin
    AContext.Connection.Disconnect;
    Exit;
  end;

  Len := AContext.Connection.IOHandler.InputBuffer.Size;
  if Len >= 200000 then
  begin
    AContext.Connection.Disconnect;
    Exit;
  end;

  // 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
        if Client.ForceDisconnect then // <-- and here, for good measure
        begin
          AContext.Connection.Disconnect;
          Exit;
        end;
        AContext.Connection.IOHandler.WriteLn(OutboundCmds.Strings[I]);
      end;
      Connection.LastSendRecv := Ticks64;
    end;

  finally
    if OutboundCmds <> nil then
    begin
      for I := 0 to OutboundCmds.Count - 1 do
      begin
        OutboundCmds.Objects[I].Free;
      end;
    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 the client wants to stay connected, it should
      // send a command every so often...
      if GetElapsedTicks(Client.LastSendRecv) >= 30000 then
      begin
        AContext.Connection.Disconnect;
        Exit;
      end;
    end;
  end;

  CMD := AContext.Connection.IOHandler.ReadLn;
  Client.LastSendRecv := Ticks64;

  ...
end;

【讨论】:

  • 有时连接卡住我在你的回答中做了同样的 onexecute 事件
  • @madammar 然后使用调试器或记录消息来找出卡住的位置。
  • 我试图在备忘录中记录断开连接程序始终执行并且客户端的 uuid 到达并且 forcedisconnect 布尔值已设置为 True
  • @madammar 1) 如果不与主 UI 线程同步,您将无法访问工作线程中的 UI 控件,并且 TCP 服务器事件在线程中触发。 2) 调试您的 OnExecute 事件代码,而不是您的断开连接代码。
  • 我在表单中没有任何 UI,我还在备忘录中添加了关键部分来记录日志,我更深入地运行服务器到调试器中,没有任何异常引发。我认为if GetElapsedTicks(Client.LastSendRecv) &gt;= 30000 then begin AContext.Connection.Disconnect; Exit; end; 的第二次检查应该替换为 forceconnect boolean ?我不确定,但我现在正在测试这个
猜你喜欢
  • 2015-05-17
  • 2013-03-31
  • 2020-05-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-13
  • 1970-01-01
相关资源
最近更新 更多