TIdTcpServer连接限制

时间:2014-02-06 10:39:38

标签: delphi indy10 tcpserver

我想限制TIdTcpServer可以接受的传入连接数,但我需要应用的规则有点复杂,所以我不认为MaxConnections属性可以用于我

我有一个运行N个服务器的应用程序,每个服务器在不同的端口上使用不同的协议。我需要限制所有N台服务器的客户端总数。例如,如果允许16个服务器和16个客户端,我会在每个服务器上允许一个客户端,或者在一台服务器上允许16个客户端。

有可能我可以动态操纵MaxConnections以解决这个问题(例如,当我确定我们'已满'时,将它们全部置为零,并且当我们未满时,将它们设置为16或其他任何东西,但是这样感觉有点过于狡猾。

我是否可以使用自己的逻辑覆盖某种虚拟IsConnectionAllowed方法,或者在连接过程中合适的位置,如果我确定已超出限制,我可以提出异常?

2 个答案:

答案 0 :(得分:1)

创建一个新组件 - 例如TIdTCPServerCollection - 它是所有服务器组件的“所有者”。

在此组件中,声明一个线程安全属性,该属性存储可用的 - 当前未使用的 - 连接计数。

在服务器连接和断开逻辑中,递减/递增此变量,并设置MaxConnections以反映新限制。

答案 1 :(得分:1)

一个选项可能是实现一个自定义TIdScheduler类,该类派生自TIdSchedulerofThread...个组件之一,并将其​​虚拟AcquireYarn()方法覆盖为:

    如果调度程序的EAbort列表已达到允许的最大连接数,则
  1. 引发ActiveYarns异常。但是,这可能会导致TIdTCPServer侦听线程中的循环过于紧张。为了减轻这种影响,您可以在方法中放置一个小计时器,并且只有在列表在短时间内保持最大值时才会引发异常。

  2. 阻止调用线程(TIdTCPServer侦听线程),直到ActiveYarns的纱线数量少于最大连接数限制,然后调用inherited方法返回新的{ {1}}通常是对象。

  3. 例如:

    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没有返回接受的客户端,一切都会正常工作,只需按预期阻止。