如何使用Indy TIdTCPServer跟踪客户端数量

时间:2011-04-01 12:52:36

标签: delphi indy tcpserver

我想知道Indy 9 TIdTCPServer的当前客户端连接数(在Delphi 2007上)

我似乎无法找到能够提供此属性的属性。

我尝试在服务器OnConnect / OnDisconnect事件上递增/递减计数器,但是当客户端断开连接时,该数字似乎永远不会减少。

有什么建议吗?

4 个答案:

答案 0 :(得分:9)

当前活动的客户端存储在服务器的Threads属性中,该属性为TThreadList。只需锁定列表,阅读其Count属性,然后解锁列表:

procedure TForm1.Button1Click(Sender: TObject);
var
  NumClients: Integer;
begin
  with IdTCPServer1.Threads.LockList do try
    NumClients := Count;
  finally
    IdTCPServer1.Threads.UnlockList;
  end;
  ShowMessage('There are currently ' + IntToStr(NumClients) + ' client(s) connected');
end;

在Indy 10中,Threads属性被Contexts属性替换:

procedure TForm1.Button1Click(Sender: TObject);
var
  NumClients: Integer;
begin
  with IdTCPServer1.Contexts.LockList do try
    NumClients := Count;
  finally
    IdTCPServer1.Contexts.UnlockList;
  end;
  ShowMessage('There are currently ' + IntToStr(NumClients) + ' client(s) connected');
end;

答案 1 :(得分:4)

不确定为什么使用OnConnect和OnDisconnect不适合您,但我们所做的是创建TIdCustomTCPServer的后代;覆盖其DoConnect和DoDisconnect方法,并创建和使用我们自己的TIdServerContext后代(一个将“提供”连接的线程后代)。

您可以通过以下方式让TIdCustomTCPServer了解您自己的TIdServerContext类:

编辑添加条件定义以显示如何使其适用于Indy9)

type
// Conditional defines so that we can use the same ancestors as in Indy10 and we
// can use the same method signatures for DoConnect and DoDisconnect regardless 
// of the Indy version. Add other conditional defines as needed.
// Note: for INDY9 to be defined, you need to include the appropriate includes 
// from Indy, or define it in your own include file.
{$IFDEF INDY9}  
  TIdContext = TIdPeerThread;
  TIdServerContext = TIdContext;
  TIdCustomTCPServer = TIdTCPServer;
{$ENDIF}

  TOurContext = class(TIdServerContext)
  private
    FConnectionId: cardinal;
  public
    property ConnectionId: cardinal ...;
  end;

...

constructor TOurServer.Create(aOwner: TComponent);
begin
  inherited Create(aOwner);

  ...
  {$IFDEF INDY10_UP}
    ContextClass := TOurContext;
  {$ELSE}
    ThreadClass := TOurContext;
  {$ENDIF}
  ...
end;

在我们的TIdCustomTCPServer后代的DoConnect重写中,我们将上下文类的ConnectionID设置为唯一值:

procedure TOurServer.DoConnect(AContext: TIdContext);
var
  OurContext: TOurContextabsolute AContext;
begin
  Assert(AContext is TOurContext);
  HandleGetNewConnectionID(OurContext, OurContext.FConnectionID);

  inherited DoConnect(AContext);

  ...

end;

我们的DoDisconnect覆盖清除ConnectionID:

procedure TOurServer.DoDisconnect(AContext: TIdContext);
var
  OurContext: TOurContextabsolute AContext;
begin
  Assert(AContext is TOurContext);
  OurContext.FConnectionID := 0;

  ...

  inherited DoDisconnect(AContext);
end;

现在可以随时获得当前连接的计数:

function TOurServer.GetConnectionCount: Integer;
var
  i: Integer;
  CurrentContext: TOurContext;
  ContextsList: TList;
begin
  MyLock.BeginRead;
  try
    Result := 0;

    if not Assigned(Contexts) then
      Exit;

    ContextsList := Contexts.LockList;
    try

      for i := 0 to ContextsList.Count - 1 do
      begin
        CurrentContext := ContextsList[i] as TOurContext;

        if CurrentContext.ConnectionID > 0 then
          Inc(Result);
      end;

    finally
      Contexts.UnLockList;
    end;
  finally
    MyLock.EndRead;
  end;
end;

答案 2 :(得分:3)

如果从OnExecute增加/减少一个计数器(或DoExecute,如果你覆盖它)怎么样?这不会出错!

如果您使用InterlockedIncrementInterlockedDecrement,则甚至不需要关键部分来保护计数器。

答案 3 :(得分:2)

这应该适用于Indy 9,但它现在已经过时了,可能在你的版本中出现了问题,尝试更新到最新的Indy 9。

我使用Indy 10进行了一个简单的测试,它在OnConnect / OnDisconnect事件处理程序中使用简单的互锁增量/减量非常有效。这是我的代码:

//closes and opens the server, which listens at port 1025, default values for all properties
procedure TForm2.Button1Click(Sender: TObject);
begin
  IdTCPServer1.Active := not IdTCPServer1.Active;
  UpdateUI;
end;

procedure TForm2.FormCreate(Sender: TObject);
begin
  UpdateUI;
end;

//Just increment the count and update the UI
procedure TForm2.IdTCPServer1Connect(AContext: TIdContext);
begin
  InterlockedIncrement(FClientCount);
  TThread.Synchronize(nil, UpdateUI);
end;

//Just decrement the count and update the UI
procedure TForm2.IdTCPServer1Disconnect(AContext: TIdContext);
begin
  InterlockedDecrement(FClientCount);
  TThread.Synchronize(nil, UpdateUI);
end;

//Simple 'X' reply to any character, A is the "command" to exit
procedure TForm2.IdTCPServer1Execute(AContext: TIdContext);
begin
  AContext.Connection.IOHandler.Writeln('Write anything, but A to exit');
  while AContext.Connection.IOHandler.ReadByte <> 65 do
    AContext.Connection.IOHandler.Write('X');
  AContext.Connection.IOHandler.Writeln('');
  AContext.Connection.IOHandler.Writeln('Good Bye');
  AContext.Connection.Disconnect;
end;

//Label update with server status and count of connected clients 
procedure TForm2.UpdateUI;
begin
  Label1.Caption := Format('Server is %s, %d clients connected', [
    IfThen(IdTCPServer1.Active, 'Open', 'Closed'), FClientCount]);
end;
然后,用telnet打开几个客户端:

3 connected clients

然后,关闭一个客户端

2 connected clients

就是这样。

INDY 10适用于Delphi 2007,我的主要建议是升级。