2012-06-12 69 views
6

在工作中,我負責將TCP服務器作爲Modbus從站設備的一部分來實現。我在堆棧交換和一般的互聯網上都做了大量的閱讀(包括優秀的http://beej.us/guide/bgnet/),但我在設計問題上掙扎。總之,我的設備只能接受2個連接,並且每個連接都會有輸入的modbus請求,我必須在主控制器循環中處理這些請求,然後回覆成功或失敗狀態。我有如何實現這一點的以下想法。在Linux上編寫多線程TCP服務器

  1. 有一個創建,結合,監聽並接受連接,偵聽器線程,然後產生一個新的並行線程監聽傳入數據和空閒超時時間後關閉連接的連接上。如果活動線程數目前爲2,則立即關閉新連接以確保只允許2個連接。

  2. 不要從偵聽器線程產生新的線程,而是使用select()檢測傳入的連接請求以及傳入的modbus連接在活動連接上(類似於Beejs指南中的方法)。

  3. 創建2個監聽器線程,每個監聽器線程創建一個可以阻止accept()調用的套接字(相同的IP和端口號),然後關閉套接字fd並處理連接。在這裏,我(可能天真地)認爲這將只允許最多2個連接,我可以使用阻塞讀取來處理。

我一直在使用C++很長一段時間,但對於Linux開發我相當陌生。對於上述哪種方法最好(如果有的話),以及如果我對Linux的經驗不足意味着他們中的任何一個都是非常糟糕的想法,我都非常歡迎。我熱衷於避免fork()並堅持pthreads,因爲傳入的modbus請求將被排隊並定期從主控制器循環讀取。提前感謝您的任何建議。

回答

3

第三種方法不起作用,只能綁定一次本地地址。

我可能會使用你的第二個選擇,除非你需要做很多處理,在這種情況下,第一個到替代的組合可能是有用的。

我想到的兩個第一個替代方法的組合是讓主線程(程序啓動時總是有的)創建兩個工作線程,然後去阻塞accept調用以等待新連接。當一個新的連接到達時,告訴其中一個線程開始處理新的連接,並返回到accept上的塊。當第二個連接被接受時,告訴另一個線程在該連接上工作。如果兩個連接都已打開,則要麼在一個連接關閉之前不接受,要麼等待新的連接但立即關閉它們。

+0

我喜歡這樣的聲音 - 唯一的問題是我的主循環嚴格禁止。它必須執行處理並定期處理來自監聽器線程的請求。考慮到這一點,你說選項2.會是最好的? – mathematician1975

+0

@ mathematician1975你仍然可以使用我的方法,但不是在'accept'上使用short或no-timeout'select'來阻塞(或者讓偵聽套接字非阻塞,並使用'accept'並檢查'EAGAIN' /'EWOULDBLOCK ')知道何時可以接受連接。 –

+0

我認爲考慮到我的時間限制,這是我在短期內追求的最佳解決方案。謝謝你的建議。 – mathematician1975

2

由於您只處理2個連接,因此每個連接的線程對於此類應用程序來說都是完美的。如果您需要擴展到數千個連接,則使用非阻塞或異步I/O的面向對象的方法會更好。 2個監聽線程很有意義,您不需要關閉接受fd。連接完成後再回來接受。事實上,一個變體是有三個線程被阻止接受。如果其中兩個線程正在主動處理連接,則第三個將重置新創建的連接(或返回忙碌響應,無論適用於您的設備)。

要使所有三個線程都在accept上阻塞,您需要讓主線程在三個線程啓動之前創建並綁定您的套接字,以執行其接受/處理處理。

man page for pthreads on Linux指示接受是線程安全的。 (線程安全函數中的部分列出了不是線程安全的函數,請參見圖)

+0

你的意思是選項3在我的問題? – mathematician1975

+0

@ mathematician1975:是的,我想到選項3的變體,但有三個線程接受。 – jxh

+0

但這裏的其他答案說我只能綁定一次,這就排除了選項3。 – mathematician1975

2

您提出的所有設計選項都不是非常面向對象的,它們都比C++更加面向C 。如果你的工作允許你使用boost,那麼Boost.Asio庫對於製作簡單(和複雜的)套接字服務器來說是非常棒的。你可以採取幾乎任何他們的例子,並簡單地擴展它只允許2個活動連接,打開後立即關閉所有其他的連接。

在我頭頂,他們的簡單HTTP服務器可以通過在連接類中保留一個靜態計數器(inc在構造函數中,dec在析構函數中)進行修改,並且在創建新的時候檢查計數並決定是否關閉連接。連接類也可以獲得boost :: asio :: deadline_timer來跟蹤超時。

這將最接近你的第一個設計選擇,提升可以在1線程中做到這一點,並在後臺做類似於select()(通常爲epoll())。但這是「C++方式」,在我看來,使用select()和原始pthread s是C方式。

+0

感謝您的建議。我完全同意你關於它的C++和OO方面的內容。然而,考慮到我目前的時間限制,我認爲我將不得不尋求原始的linux API方法,因爲它讓我花了時間去適應它並需要快速獲得原型。但是,一旦項目被接受,我想我肯定會贊成這種方法 – mathematician1975