網路程式設計常見問題總結

2021-08-25 16:10:02 字數 3197 閱讀 3232

在網路程式設計中對於乙個網路控制代碼會遇到阻塞io和非阻塞io的概念, 這裡對於這兩種socket先做一下說明

基本概念:socket的阻塞模式意味著必須要做完io操作(包括錯誤)才會返回。 非阻塞模式下無論操作是否完成都會立刻返回,需要通過其他方式來判斷具體操作是否成功。

設定:一般對於乙個socket是阻塞模式還是非阻塞模式有兩種方式 fcntl設定和recv,send系列的引數.

fcntl函式可以將乙個socket控制代碼設定成非阻塞模式:

flags = fcntl(sockfd, f_getfl, 0); fcntl(sockfd, f_setfl, flags | o_nonblock); 設定之後每次的對於sockfd的操作都是非阻塞的

recv, send函式的最後有乙個flag引數可以設定成msg_dontwait臨時將sockfd設定為非阻塞模式,而無論原有是阻塞還是非阻塞。 recv(sockfd, buff, buff_size, msg_dontwait); send(scokfd, buff, buff_size, msg_dontwait);

區別:

讀: 讀本質來說其實不能是讀,在實際中, 具體的接收資料不是由這些呼叫來進行,是由於系統底層自動完成的,read也好,recv也好只負責把資料從底層緩衝copy到我們指定的位置. 對於讀來說(read, 或者 recv) ,在阻塞條件下如果沒有發現資料在網路緩衝中會一直等待,當發現有資料的時候會把資料讀到使用者指定的緩衝區,但是如果這個時候讀到的資料量比較少,比引數中指定的長度要小,read並不會一直等待下去,而是立刻返回。read的原則是資料在不超過指定的長度的時候有多少讀多少,沒有資料就會一直等待。所以一般情況下我們讀取資料都需要採用迴圈讀的方式讀取資料,一次read完畢不能保證讀到我們需要長度的資料,read完一次需要判斷讀到的資料長度再決定是否還需要再次讀取。在非阻塞的情況下,read的行為是如果發現沒有資料就直接返回,如果發現有資料那麼也是採用有多少讀多少的進行處理.對於讀而言,阻塞和非阻塞的區別在於沒有資料到達的時候是否立刻返回.

recv中有乙個msg_waitall的引數 recv(sockfd, buff, buff_size, msg_waitall), 在正常情況下 recv是會等待直到讀取到buff_size長度的資料,但是這裡的waitall也只是盡量讀全,在有中斷的情況下recv還是可能會被打斷,造成沒有讀完指定的buff_size的長度。所以即使是採用recv + waitall引數還是要考慮是否需要迴圈讀取的問題,在實驗中對於多數情況下recv還是可以讀完buff_size,所以相應的效能會比直接read進行迴圈讀要好一些。不過要注意的是這個時候的sockfd必須是處於阻塞模式下,否則waitall不能起作用。

寫: 寫的本質也不是進行傳送操作,而是把使用者態的資料copy到系統底層去,然後再由系統進行傳送操作,返回成功只表示資料已經copy到底層緩衝,而不表示資料以及發出,更不能表示對端已經接收到資料.

對於write(或者send)而言,在阻塞的情況是會一直等待直到write完全部的資料再返回.這點行為上與讀操作有所不同,究其原因主要是讀資料的時候我們並不知道對端到底有沒有資料,資料是在什麼時候結束傳送的,如果一直等待就可能會造成死迴圈,所以並沒有去進行這方面的處理;而對於write, 由於需要寫的長度是已知的,所以可以一直再寫,直到寫完.不過問題是write是可能被打斷造成write一次只write一部分資料, 所以write的過程還是需要考慮迴圈write, 只不過多數情況下一次write呼叫就可能成功.

非阻塞寫的情況下,是採用可以寫多少就寫多少的策略.與讀不一樣的地方在於,有多少讀多少是由網路傳送的那一端是否有資料傳輸到為標準,但是對於可以寫多少是由本地的網路堵塞情況為標準的,在網路阻塞嚴重的時候,網路層沒有足夠的記憶體來進行寫操作,這時候就會出現寫不成功的情況,阻塞情況下會盡可能(有可能被中斷)等待到資料全部傳送完畢, 對於非阻塞的情況就是一次寫多少算多少,沒有中斷的情況下也還是會出現write到一部分的情況.

超時控制:

對於網路io,我們一般情況下都需要超時機制來避免進行操作的執行緒被handle住, 經典的做法就是採用select+非阻塞io進行判斷, select在超時時間內判斷是否可以讀寫操作,然後採用非堵塞讀寫,不過一般實現的時候讀操作不需要設定為非堵塞,上面已經說過讀操作只有在沒有資料的時候才會阻塞,select的判斷成功說明存在資料,所以即使是阻塞讀在這種情況下也是可以做到非阻塞的效果,就沒有必要設定成非阻塞的情況了.

這部分的**可以參考ullib中ul_sreado_ms_ex和ul_swriteo_ms_ex.

採用ul_sreado_ms_ex讀資料也是不能保證返回大於0就一定讀到指定的資料長度, 對於讀寫操作, 都是需要判斷返回的讀長度或者寫長度是否是需要的長度, 不能簡單的判斷一下返回值是否小於0. 對於ul_sreado_ms_ex的情況如果出現了傳送端資料傳送一半就被close掉的情況就有可能導致接收端讀不到完整的資料報.

errno 只有在函式返回值為負的時候才有效,如果返回0或者大於0的數, errno 的結果是無意義的. 有些時候 會出現read到0, 但是我們認為是錯誤的情況然後輸出errno造成誤解,一般建議在這種情況要同時輸出返回值和errno的結果,有些情況由於只有errno造成了對於問題的判斷失誤。

長連線和短連線的各種可能的問題及相應的處理

這裡主要是發起連線的客戶端的問題,這裡列出的問題主要是在採用同步模型的情況下才會存在的問題.

短連線:

長連線相比短連線減少了連線的時間消耗, 可以承受更高的負載. 但在使用的時候需要考慮一些問題髒資料, 在一些特殊情況(特別是邏輯錯誤的情況下) 會存在一些我們並不需要的資料. 這個時候的處理比較安全的方式是一旦檢測到就關閉連線, 檢測的方式在在發起請求前 用 前面 為什麼socket寫錯誤,但用recv檢查依然成功? 介紹的方式進行檢查. 不過有些程式會採用繼續讀把所有不需要的資料讀完畢(讀到 eaegin), 不過這種方式過分依賴邏輯了,存在了一定的風險. 不如直接斷開來的簡單 後端連線, 前面也提到了 在這種情況我們一般會採用連線池的方式來解決問題比如(public/connectpool中就可以維護不同的連線,使每個執行緒都可以均勻的獲取到控制代碼) 服務端的處理這個時候需要考慮連線的數量,簡單的方式就是乙個長連線乙個執行緒, 但是執行緒也不能無限增加( 增加了,可能造成大量的上下文切換使的效能下降). 我們一般在長連線的情況採用pendingpool的模型, 通過乙個非同步佇列來緩衝, 這樣不需要考慮客戶端和服務端的執行緒數問題,可以任意配置(可以通過線下測試選擇合適的執行緒數)

一些特殊的問題, 主要是長連線的延時 在後面的faq中會有詳細的說明.

一般來說,對於我們多數的內部業務邏輯都是可以採用長連線模式,不會產生太多的問題.

網路程式設計常見問題總結

o y k h m b1 o r 對於網路程式設計的更多詳細說明建議參考下面的書籍 unix網路程式設計 tcp ip 詳解 unix環境高階程式設計 f0 i h,r v q 非阻塞io和阻塞io l r i3 h8 b 在網路程式設計中對於乙個網路控制代碼會遇到阻塞io和非阻塞io的概念,這裡對...

網路程式設計常見問題總結

對於網路程式設計的更多詳細說明建議參考下面的書籍 unix網路程式設計 tcp ip 詳解 unix環境高階程式設計 非阻塞io和阻塞io 在網路程式設計中對於乙個網路控制代碼會遇到阻塞io和非阻塞io的概念,這裡對於這兩種socket先做一下說明 基本概念 socket的阻塞模式意味著必須要做完i...

網路程式設計常見問題總結

對於網路程式設計的更多詳細說明建議參考下面的書籍 unix網路程式設計 tcp ip 詳解 unix環境高階程式設計 非阻塞io和阻塞io 在網路程式設計中對於乙個網路控制代碼會遇到阻塞io和非阻塞io的概念,這裡對於這兩種socket先做一下說明 基本概念 socket的阻塞模式意味著必須要做完i...