[英]TIdTcpServer connection limiting
我想限制TIdTcpServer
可以接受的傳入連接數,但我需要應用的規則有點復雜,所以我不認為MaxConnections
屬性對我有效。
我有一個運行N個服務器的應用程序,每個服務器在不同的端口上使用不同的協議 我需要限制所有N台服務器的客戶端總數。 例如,如果允許16個服務器和16個客戶端,我會在每個服務器上允許一個客戶端,或者在一台服務器上允許16個客戶端。
有可能我可以動態地操縱MaxConnections
以解決這個問題(例如,當我確定我們'滿'時,將它們全部設置為零,然后回到16或者當我們沒有滿的時候將它們設置為零,但這感覺有點過於棘手。
是否存在某種虛擬IsConnectionAllowed
方法,我可以使用自己的邏輯覆蓋,或者在連接過程中合適的位置,如果我確定已超出限制,我可以引發異常?
創建一個新組件 - 例如TIdTCPServerCollection - 它是所有服務器組件的“所有者”。
在此組件中,聲明一個線程安全屬性,該屬性存儲可用的 - 當前未使用的 - 連接計數。
在服務器連接和斷開邏輯中,遞減/遞增此變量,並設置MaxConnections以反映新限制。
一種選擇可能是實現從TIdSchedulerofThread...
組件之一派生的自定義TIdScheduler
類,並將其虛擬AcquireYarn()
方法覆蓋為:
如果調度程序的ActiveYarns
列表已達到允許的最大連接數,則引發EAbort
異常。 但是,這可能會導致TIdTCPServer
監聽線程中的循環過於緊張。 為了減輕這種影響,您可以在方法中放置一個小計時器,如果列表在短時間內保持最大值,則只會引發異常。
阻止調用線程( 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.