我想知道当前客户端与 Indy 9 TIdTCPServer 的连接数(在 Delphi 2007 上)
我似乎找不到提供这个的属性。
我尝试在服务器 OnConnect/OnDisconnect 事件上增加/减少一个计数器,但是当客户端断开连接时,这个数字似乎永远不会减少。
有什么建议么?
当前活动的客户端存储在服务器的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;
不知道为什么使用 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;
如何增加/减少一个计数器OnExecute
(或者DoExecute
如果你覆盖它)?那不能出错!
如果您使用InterlockedIncrement
并且InterlockedDecrement
您甚至不需要关键部分来保护计数器。
这应该适用于 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 打开几个客户端:
然后,关闭一个客户
就是这样。
INDY 10 适用于 Delphi 2007,我的主要建议是无论如何都要升级。