我想限制TIdTcpServer
可以接受的传入连接数,但我需要应用的规则有点复杂,所以我不认为MaxConnections
属性可以用于我
我有一个运行N个服务器的应用程序,每个服务器在不同的端口上使用不同的协议。我需要限制所有N台服务器的客户端总数。例如,如果允许16个服务器和16个客户端,我会在每个服务器上允许一个客户端,或者在一台服务器上允许16个客户端。
有可能我可以动态操纵MaxConnections
以解决这个问题(例如,当我确定我们'已满'时,将它们全部置为零,并且当我们未满时,将它们设置为16或其他任何东西,但是这样感觉有点过于狡猾。
我是否可以使用自己的逻辑覆盖某种虚拟IsConnectionAllowed
方法,或者在连接过程中合适的位置,如果我确定已超出限制,我可以提出异常?
答案 0 :(得分:1)
创建一个新组件 - 例如TIdTCPServerCollection - 它是所有服务器组件的“所有者”。
在此组件中,声明一个线程安全属性,该属性存储可用的 - 当前未使用的 - 连接计数。
在服务器连接和断开逻辑中,递减/递增此变量,并设置MaxConnections以反映新限制。
答案 1 :(得分:1)
一个选项可能是实现一个自定义TIdScheduler
类,该类派生自TIdSchedulerofThread...
个组件之一,并将其虚拟AcquireYarn()
方法覆盖为:
EAbort
列表已达到允许的最大连接数,则引发ActiveYarns
异常。但是,这可能会导致TIdTCPServer
侦听线程中的循环过于紧张。为了减轻这种影响,您可以在方法中放置一个小计时器,并且只有在列表在短时间内保持最大值时才会引发异常。
阻止调用线程(TIdTCPServer
侦听线程),直到ActiveYarns
的纱线数量少于最大连接数限制,然后调用inherited
方法返回新的{ {1}}通常是对象。
例如:
TIdYarn
然后将此类的单个实例分配给所有服务器的type
TMyScheduler = class(TIdSchedulerOfThreadDefault)
public
function AcquireYarn: TIdYarn; override;
end;
function TMyScheduler.AcquireYarn: TIdYarn;
begin
if not ActiveYarns.IsCountLessThan(SomeLimit) then
begin
Sleep(1000);
if not ActiveYarns.IsCountLessThan(SomeLimit) then
Abort;
end;
Result := inherited;
end;
属性。 Scheduler
在接受新的客户端连接之前调用TIdTCPServer
。
另一个选项,仅适用于Windows,是从AcquireYarn()
派生新的TIdStack
类,并覆盖其虚拟TIdStackWindows
方法,以使用Winsock的WSAAccept()
函数而不是Accept()
功能。 accept()
允许您分配一个回调函数,该函数根据传递给回调的条件(QOS等)决定是接受还是拒绝新客户端。该回调可以检查您为正在运行的活动连接数维护的全局计数器(或者只是总结所有服务器的活动WSAAccept()
计数),然后如果已达到限制则返回Contexts
,否则返回CF_REJECT
。然后,您可以使用CF_ACCEPT
单元中的SetStackClass()
函数将您的类指定为所有Indy套接字连接的活动堆栈。
例如:
IdStack
这样,Indy根本不会看到任何被拒绝的客户端连接,并且您不必担心在type
TMyStack = class(TIdStackWindows)
public
function Accept(ASocket: TIdStackSocketHandle; var VIP: string; var VPort: TIdPort; var VIPVersion: TIdIPVersion): TIdStackSocketHandle; override;
end;
function MyAcceptCallback(lpCallerId: LPWSABUF; lpCallerData: LPWSABUF; lpSQOS, pGQOS: LPQOS; lpCalleeId, lpCalleeData: LPWSABUF; g: PGROUP; dwCallbackData: DWORD_PTR): Integer; stdcall;
begin
if NumActiveConnections >= SomeLimit then
Result := CF_REJECT
else
Result := CF_ACCEPT;
end;
function TMyStack.Accept(ASocket: TIdStackSocketHandle; var VIP: string; var VPort: TIdPort; var VIPVersion: TIdIPVersion): TIdStackSocketHandle;
var
LSize: Integer;
LAddr: SOCKADDR_STORAGE;
begin
LSize := SizeOf(LAddr);
//Result := IdWinsock2.accept(ASocket, IdWinsock2.PSOCKADDR(@LAddr), @LSize);
Result := IdWinsock2.WSAAccept(ASocket, IdWinsock2.PSOCKADDR(@LAddr), @LSize, @MyAcceptCallback, 0);
if Result <> INVALID_SOCKET then begin
case LAddr.ss_family of
Id_PF_INET4: begin
VIP := TranslateTInAddrToString(PSockAddrIn(@LAddr)^.sin_addr, Id_IPv4);
VPort := ntohs(PSockAddrIn(@LAddr)^.sin_port);
VIPVersion := Id_IPv4;
end;
Id_PF_INET6: begin
VIP := TranslateTInAddrToString(PSockAddrIn6(@LAddr)^.sin6_addr, Id_IPv6);
VPort := ntohs(PSockAddrIn6(@LAddr)^.sin6_port);
VIPVersion := Id_IPv6;
end;
else begin
CloseSocket(Result);
Result := INVALID_SOCKET;
IPVersionUnsupported;
end;
end;
end;
end;
initialization
SetStackClass(TMyStack);
或其各种依赖项中实现任何其他黑客攻击。只要TIdTCPServer
没有返回接受的客户端,一切都会正常工作,只需按预期阻止。