2009-10-06 93 views
0

比方說,我有一個服務器程序,可以接受來自10個(或更多)不同客戶端的連接。客戶隨機發送服務器收到的數據,但可以肯定的是每次更新至少有一個客戶端會發送數據。服務器不能等待信息到達,因爲它有其他處理要做。除了使用異步套接字外,我還看到了兩個選項:使用多個套接字,是非阻塞或阻塞與選擇更好?

  1. 使所有套接字非阻塞。在一個循環中,在每個套接字上調用recv(),並且如果沒有可用的數據並且如果我碰巧獲取一些數據,則允許它與WSAEWOULDBLOCK一起失敗,然後保留它。

  2. 將套接字留作阻塞。將所有插座添加到FD_SET並致電select()。如果返回值不爲零(大部分時間),請遍歷所有套接字以找到適當數量的可讀套接字,其中FD_ISSET()只能在可讀套接字上調用recv()

第一個選項將創建更多的recv()函數調用。第二種方法是從編程的角度來看更大的痛苦,因爲所有的FD_SETFD_ISSET循環。

哪種方法(或其他方法)是首選?避免讓recv()在無阻塞的套接字上失敗的開銷值得稱呼select()的麻煩?

我想我理解了這兩種方法,並且我都嘗試了成功,但我不知道是否有一種方法被認爲是更好或最優。

回答

1

我會推薦使用overlapped IO來代替。然後,您可以啓動WSARecv(),並提供一個回調函數,以在操作完成時調用。更重要的是,因爲只有在程序處於可警告等待狀態時纔會調用它,所以您不必擔心像在線程應用程序中那樣的鎖定(假設您在主線程上運行它們)。

但是,請注意,您需要經常進入這樣一個可警告的等待狀態。如果這是您的UI線程,請確保在消息循環中使用MsgWaitForMultipleObjectsEx(),並使用MWMO_ALERTABLE標誌。這會給你的回調機會。在非UI線程中,定期調用wait functions中的任何一個,使您進入可警告的等待狀態。

還要注意模態對話通常不會進入可警告的等待狀態,因爲它們有自己的消息循環,它不會調用MsgWaitForMultipleObjectsEx()。如果在顯示對話框時需要處理網絡IO,請在專用線程上執行所有網絡IO,該線程會定期進入可警告的等待狀態。

如果出於某種原因無法使用重疊IO - 絕對使用阻止select()。像無限循環那樣使用非阻塞recv()是CPU時間不可原諒的浪費。但是,請確保套接字處於非阻塞模式 - 否則,如果一個字節到達並且您嘗試讀取兩個,您可能會意外阻塞。

您可能還想考慮使用庫來抽象出挑剔的細節。例如,libeventboost::asio

+0

謝謝。對於這個實現,我不想使用Overlapped IO,但是謝謝你的建議。我會研究這一點。我瞭解你的建議,使用非阻塞模式和選擇()。我的主要問題是我應該無緣無故地致電recv,或者通過調用select()的動作。謝謝回覆! – JPhi1618 2009-10-06 17:30:37

+0

更新了一點,以便更清楚 - 只需循環「recv()」即可將CPU固定在100%,這絕不是一件好事。 – bdonlan 2009-10-06 21:02:53

0

IO應該完全阻塞每個連接一個線程,在這種情況下,事件循環本質上是一個操作系統調度器或IO應該是完全非阻塞的,在這種情況下,基於select/waitformultipleobjects的事件循環將會在你的應用程序

所有的中間變量都不是很容易維護和容易出錯

完全無阻塞的方式擴展更好的併發連接數量增長,沒有一個線程上下文切換開銷,所以它是一個在併發連接數不固定的情況下最好。與完全阻止其相比,這種方法具有更高的實施複雜性。

對於完全非阻塞IO應用程序的核心是一個基於select/waitformultipleobjects的事件循環,所有套接字都處於非阻塞模式,所有讀/寫通常是在事件循環線程內完成的(對於頂層性能寫入可以首先嚐試直接從請求寫入的線程)