2012-01-29 40 views
1

我想實現一個TCP服務器,這是一個較大的項目的一部分。基本上,服務器應該能夠與任意數量的客戶端(至少32)保持TCP連接,併爲請求服務的任何客戶端提供服務。在我們的場景中,事情是假設一旦客戶端連接到服務器,它將永遠不會關閉連接,除非發生某種故障(例如,運行客戶端的機器故障),並且它會重複請求服務服務器。所有其他客戶端i-e也是如此,每個客戶端都將維護與服務器的連接並執行交易。因此總結一下,服務器將同時維護與客戶端的連接,同時根據需要爲每個客戶端提供服務,並且還應該能夠接受任何想要連接到服務器的其他客戶端連接。多線程服務器設計

現在我使用berkely套接字API的select()系統調用實現了上述功能,並且當我們有少量客戶端(比如說10)時,它工作正常。但是,當我們在一臺16核心機器上實現它時,服務器需要儘可能地擴展到最高級別。爲此,我通過各種多線程設計技術來查看e-g每個客戶端的一個線程等,我認爲最好的一個線程池設計。現在我正要實現我遇到了一些問題: 如果我指定主線程接受任何數量的傳入連接並將每個連接文件描述符保存在數據結構中,並且我有一個線程池,那麼我將如何獲取線程來輪詢某個特定客戶是否請求服務。該設計足夠簡單,適用於客戶端與服務器聯繫的場景,並且在獲得服務後,它會關閉連接,以便我們可以從池中選擇一個線程,爲客戶端提供服務,然後將其推回池中以供將來連接處理。但是,當我們必須服務一組維護連接並間歇性請求服務的客戶端時,最好的方法是做什麼。所有的幫助將非常感謝,因爲我真的被困在這。 謝謝。

+0

粘貼你的代碼,讓我們看看。 Apache有什麼問題? – YumYumYum 2012-01-29 21:04:37

+0

我必須使用SOCKET API在系統無關的C語言中開發服務器。 – Abdullah 2012-01-29 21:07:56

回答

1

使用pthreads,每個CPU一個線程加一個額外的線程。

附加線程(主線程)偵聽與listen()系統調用的新連接,接受帶有accept()的新連接,然後確定哪個工作線程當前具有最少的連接數,獲取鎖/該工作線程的「掛起連接」FIFO隊列的互斥量,將接受連接的描述符放到工作線程的「掛起連接」FIFO隊列中,並向工作線程發送「檢查隊列」通知(例如使用管道)。

工作線程使用「select()」,併發送/接收數據到他們接受的任何連接。如果/當某個工作線程從主線程收到「檢查隊列」通知時,它將獲取其「等待連接」FIFO隊列的鎖定/互斥並將任何新接受的連接添加到其「fd_set」列表中。

對於1024個連接和16個CPU;你最終可能會有一個主線程等待新的連接(但幾乎不做任何事情,因爲你不會期待很多新的連接),而16個工作線程每個平均處理64個連接。

+0

這種傳遞連接的東西過於複雜,並沒有任何好處,只是空閒線程直接調用'accept'。 – 2012-01-30 00:38:16

+0

@R:我寧願有阻止等待「select()」的線程,而不是線程經常浪費CPU時間輪詢「accept()」和「select()」,或者需要長時間響應的線程「accept )「,因爲他們正在等待」select()「超時。阻塞等待「select()」的線程可以從主線程接收到通過發送的連接請求。管道;這意味着你的工作線程不會解除阻塞,除非他們有真正的工作要做(不輪詢)。 – Brendan 2012-01-30 04:56:46

+0

Thinks alot @ Brendan。我會嘗試,當然,這是有道理的。 – Abdullah 2012-01-30 11:01:07

0

每個客戶端的一個線程幾乎肯定是最好的設計。確保至少有一個線程在accept中等待新連接時被阻止 - 這意味着在接受成功之後,如果它是最後一個,那麼在繼續之前可能需要創建一個新線程。我發現信號量是跟蹤產生新的偵聽線程的需要的一個偉大的原始信號。

+0

「每個客戶端一個線程」的問題是,在重負載所有這些線程都會爭奪有限數量的CPU,這會增加延遲。如果您需要響應1000個接收數據包,最好處理500個數據包,500個數據包未啓動,而不是1000個「半處理」數據包(並且考慮到線程切換的成本,用於鎖定和其他可伸縮性問題)。 – Brendan 2012-01-30 04:48:38

+0

線程之間的上下文切換成本低於大多數系統調用(我測量過;請參閱我的一個問題)。 – 2012-01-30 07:37:10