簡體   English   中英

TIdTcpServer連接限制

[英]TIdTcpServer connection limiting

我想限制TIdTcpServer可以接受的傳入連接數,但我需要應用的規則有點復雜,所以我不認為MaxConnections屬性對我有效。

我有一個運行N個服務器的應用程序,每個服務器在不同的端口上使用不同的協議 我需要限制所有N台服務器的客戶端總數。 例如,如果允許16個服務器和16個客戶端,我會在每個服務器上允許一個客戶端,或者在一台服務器上允許16個客戶端。

有可能我可以動態地操縱MaxConnections以解決這個問題(例如,當我確定我們'滿'時,將它們全部設置為零,然后回到16或者當我們沒有滿的時候將它們設置為零,但這感覺有點過於棘手。

是否存在某種虛擬IsConnectionAllowed方法,我可以使用自己的邏輯覆蓋,或者在連接過程中合適的位置,如果我確定已超出限制,我可以引發異常?

創建一個新組件 - 例如TIdTCPServerCollection - 它是所有服務器組件的“所有者”。

在此組件中,聲明一個線程安全屬性,該屬性存儲可用的 - 當前未使用的 - 連接計數。

在服務器連接和斷開邏輯中,遞減/遞增此變量,並設置MaxConnections以反映新限制。

一種選擇可能是實現從TIdSchedulerofThread...組件之一派生的自定義TIdScheduler類,並將其虛擬AcquireYarn()方法覆蓋為:

  1. 如果調度程序的ActiveYarns列表已達到允許的最大連接數,則引發EAbort異常。 但是,這可能會導致TIdTCPServer監聽線程中的循環過於緊張。 為了減輕這種影響,您可以在方法中放置一個小計時器,如果列表在短時間內保持最大值,則只會引發異常。

  2. 阻止調用線程( TIdTCPServer監聽線程),直到ActiveYarns的紗線數量少於最大連接數限制,然后調用inherited方法正常返回新的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在接受新的客戶端連接之前調用AcquireYarn()

另一個選項,僅適用於Windows,是從TIdStackWindows派生一個新的TIdStack類,並覆蓋其虛擬的Accept()方法,以使用Winsock的WSAAccept()函數而不是其accept()函數。 WSAAccept()允許您分配一個回調函數,該函數根據傳遞給回調(QOS等)的條件決定是接受還是拒絕新客戶端。 該回調可以檢查您為運行的活動連接數量而維護的全局計數器(或者只是總結所有服務器的活動Contexts計數),然后在達到限制時返回CF_REJECT ,否則返回CF_ACCEPT 然后,您可以使用IdStack單元中的SetStackClass()函數將您的類指定為所有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);

這樣,Indy將永遠不會看到任何被拒絕的客戶端連接,並且您不必擔心在TIdTCPServer或其各種依賴項中實現任何其他hack。 只要TIdStack.Accept()沒有返回接受的客戶端,一切都會正常工作並按預期TIdStack.Accept()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM