TCP 端口监听队列原理
近期需要實現一個TCP線程池服務,該服務需要能夠在同一個端口上實現 TCP 常規服務、HTTP請求服務、SOAP WebService 服務,為了測試 ACE 的線程池啟動后,如果所有線程都在忙,客戶端的連接是否還能夠建立,特實現了一個簡單的測試程序,如下:
ACE_TP_TEST#include?"stdafx.h"
//==================================================================================================
#define?DPS_DEBUG(?fmt?,??)?ACE_DEBUG((?LM_DEBUG?,?ACE_TEXT(?"%T?:?"?fmt?"\n"?)?,?__VA_ARGS__?))
#define?DPS_ERROR(?fmt?,??)?ACE_ERROR((?LM_ERROR?,?ACE_TEXT(?"%T?:?"?fmt?"\n"?)?,?__VA_ARGS__?))
//==================================================================================================
//????網絡流式套接字服務包裝類:自動將網絡輸入和關閉事件通知到連接管理類
struct?TPS_Servicer?:?public?ACE_Svc_Handler<?ACE_SOCK_STREAM?,?ACE_NULL_SYNCH?>
{
????virtual?int?handle_input(?ACE_HANDLE?Handler?=?ACE_INVALID_HANDLE?)
????{
????????DPS_DEBUG(?"Block?Handle?Input?&?Sleeping??"?,?0?);
????????while(?true?)?{?ACE_OS::sleep(?1000?);?}
????????return?0?;
????}
};
//==================================================================================================
//????網絡流式套接字服務監聽包裝類:自動建立端口監聽并接受連接同時將連接服務轉交給對應的類
struct?TPS_Acceptor?:?public?ACE_Acceptor<?TPS_Servicer?,?ACE_SOCK_ACCEPTOR?>
{
????typedef?TPS_Servicer?Servicer_Handler?;
????typedef?ACE_Acceptor<?TPS_Servicer?,?ACE_SOCK_ACCEPTOR?>?Acceptor_Handler?;
????TPS_Acceptor(?ACE_INET_Addr?&?rAddress?)?:?Acceptor_Handler(?rAddress?)?{?}
????virtual?int?handle_input?(?ACE_HANDLE?hSock?)
????{
????????return?Acceptor_Handler::handle_input(?hSock?);
????}
};
//==================================================================================================
//????發動機的線程池包裝類(ACE_Reactor?ThreadPool)
struct?TPS_ServerReactor?:?public?ACE_Task_Base
{
????//????給定線程池中線程數目啟動反應器線程池
????inline?TPS_ServerReactor(?long?nFlags?,?int?nThreads?=?16?)
????{
????????if(?activate(?THR_JOINABLE?|?nFlags?,?nThreads?)?==?-1?)
????????{
????????????DPS_ERROR(?"%T?:?Start?ThreadPool(%d)?failed"?,?nThreads?);
????????}
????}
????//????進行反應堆的事件處理
????virtual?int?svc()?{?return?ACE_Reactor::run_event_loop();?}????
};
//==============================================================================
//????程序處理的主函數
ACE_INT32?ACE_TMAIN(?ACE_INT32?nArg?,?ACE_TCHAR?*?lpArg[]?)
{
????//????設置多線程運行環境
????ACE_TP_Reactor?xtReactor;
????ACE_Reactor?xReactor(?&?xtReactor?);
????ACE_Reactor::instance(?&?xReactor?);
????ACE_ASSERT(?xReactor.initialized(?)?);
????//????建立網絡端口的監聽
????ACE_INET_Addr?iNetAddr(?(ACE_UINT16)80?);
????TPS_Acceptor?xRemoteAcceptor(?iNetAddr?);
????//????啟動線程池開始服務
????TPS_ServerReactor?xProcessor(?THR_NEW_LWP?,?4?);????
????ACE_Thread_Manager::instance(?)->wait(?);
????return?xProcessor.wait(?);
}
啟動程序,該程序建立一個包含四個線程的線程池提供 TCP 服務,在 80 端口上進行監聽,在客戶端使用 TELNET 來進行測試,首先啟動四個客戶端,并且輸入一個字母,讓服務器的四個線程都進行繁忙狀態,然后在啟動一個客戶端,依然能夠進行連接,想起 TCP 監聽端口在有連接請求過來時是有一個隊列進行緩沖的,該值默認是 5,所以在啟動五個客戶端,這樣4個在繁忙,五個進入了緩沖隊列,最后一個應該連接不上了吧?結果發現啟動了幾十個客戶端全部都連上了,此時服務器的線程池都在“繁忙”中,主線程在等待線程池結束,那么是誰接受了客戶端的連接?郁悶不解中.......
查看 ACE 的源代碼,發現在建立 TCP 端口監聽時,對緩沖隊列的大小設置是通過宏 ACE_DEFAULT_BACKLOG 來指定的,該宏在 Windows 上使用系統定義的 SOMAXCONN ,(在其他系統上使用默認值 5) , 而 SOMAXCONN 的定義是 0x7fffffff , (當然實際上不可能有這么多的連接到達,即使有緩沖隊列也不可能容納的下,因為幾乎不可能有那么大的內存來做緩沖)查閱 MSDN 說使用該值,系統將盡可能的緩沖所有的客戶端連接請求,就是說在 Windows 上,只要服務器系統能力允許,幾十服務器上的程序繁忙,客戶端的連接請求幾乎都能夠被系統緩存下來,客戶端不會發生連接失敗的情況;
最后在 TCP/IP 詳解一書中的 194 頁找到了相關的描述,在建立 TCP 的端口監聽時可以指定一個連接的緩沖隊列,該隊列的長度上限在 Unix 和早期的 Windows 上默認實現都是 5 個,在新版本的 Windows 中該隊列的上限系統不再做限制;
當客戶端發起連接時,首先是 TCP 接受了該連接,然后應用程序接受該連接,如果應用程序忙沒有即時接受連接(即將該連接從 TCP 接受的連接隊列中移走)那么只有在隊列滿后 TCP 將不再接受客戶端的連接,所以此時服務器程序即使處于繁忙中,客戶端的連接因為 TCP 接受了,雖然服務器程序沒有接受該連接,但是客戶端的表現是連接已經建立,但是發送任何內容給服務器都沒有反映(因為服務器程序沒有接受該連接);
呵呵,原來如此......
轉載于:https://www.cnblogs.com/WonKerr/archive/2009/03/04/TCP_Listen_Queue.html
總結
以上是生活随笔為你收集整理的TCP 端口监听队列原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 录入学员的身份证后控件焦点转移时根据身份
- 下一篇: C#索引器(一)