TCP和UDP阻塞和非阻塞之間的區別


首先socket在默認情況下是阻塞狀態的,這就使得發送以及接收操作處於阻塞的狀態,即調用不會立即返回,而是進入睡眠等待操作完成。下面把討論點分為發送以及接收。

  一.發送選用send(這里特指TCP)以及sendto(這里特指UDP)來描述

    首先需要說明的是,不管阻塞還是非阻塞,在發送時都會將數據從應用緩沖區拷貝到內核緩沖區(SO_RCVBUF選項聲明,除非緩沖區大小為0)。我在網絡上看到某些人說,阻塞就是將數據真正發送給對方,並且阻塞是發生在需要把前面的所有數據全部發送出去,然后再發送本次的,而非阻塞則是拷貝到發送緩沖區。我不得不說,上面的這種說法是錯誤的。

    在阻塞模式下send操作將會等待所有數據均被拷貝到發送緩沖區后才會返回。

    例如:如果當前發送緩沖總大小為8192,已經拷貝到緩沖的數據為8000,那剩余的大小為192,現在需要發送2000字節數據,那阻塞發送就會等待緩沖區足夠把所有2000字節數據拷貝進去,如第一次拷貝進192字節,當緩沖區成功發送出1808字節后,再把應用緩沖區剩余的1808字節拷貝到內核緩沖,而后send操作返回成功發送字節數。

    從上面的過程不難看出,阻塞的send操作返回的發送大小,必然是你參數中的發送長度的大小。

 man 一下 send,發現man里有描述容易誤導,send() shall fail.應該是需要自己判斷的,會返回實際發送成功的字節,想全部成功發送,需要自己實現一遍阻塞循環發送的邏輯,當多端口*大包發送的時候,可以非阻塞拆包發送(循環每個端口send一次小包,避免單個端口阻塞很久),效果會比阻塞的好很多。

關於拆分大包的一些實施方法,(一般是圖片和視頻),有人在應用層分片,針對50K的默認發送緩沖區和4k以上的滑動窗口來做文章,動態調整每次send的size在4K到50k間浮動,來減少發包的分片,通過減少分片重組的次數來優化網絡服務的總延時。至於MSS一般是1460字節,為何選4k,而不是1.4k起,是因為這會造成多次RTT消耗以及用戶態和內核態切換,得不償失。

    在阻塞模式下的sendto操作不會阻塞。

    關於這一點的原因在於:UDP並沒有真正的發送緩沖區,它所做的只是將應用緩沖區拷貝給下層協議棧,在此過程中加上UDP頭,IP頭,所以實際不存在阻塞。

    在非阻塞模式下send操作調用會立即返回。

   例如:當緩沖區只有192字節,但是卻需要發送2000字節時,此時調用立即返回,並得到返回值為192。從中可以看到,非阻塞send僅僅是盡自己的能力向緩沖區拷貝盡可能多的數據,因此在非阻塞下send才有可能返回比你參數中的發送長度小的值。

    如果緩沖區沒有任何空間時呢?這時肯定也是立即返回,但是你會得到WSAEWOULDBLOCK/E WOULDBLOCK 的錯誤,此時表示你無法拷貝任何數據到緩沖區,你最好休息一下再嘗試發送。

    在非阻塞模式下sendto操作 不會阻塞(與阻塞一致,不作說明)。

 

  二.接收選用recv(這里特指TCP)以及recvfrom(這里特指UDP)來描述

    在阻塞模式下recv,recvfrom操作將會阻塞 到緩沖區里有至少一個字節(TCP)或者一個完整UDP數據報才返回。

    在沒有數據到來時,對它們的調用都將處於睡眠狀態,不會返回。

    在非阻塞模式下recv,recvfrom操作將會立即返回。

    如果緩沖區 有任何一個字節數據(TCP)或者一個完整UDP數據報,它們將會返回接收到的數據大小。而如果沒有任何數據則返回錯誤 WSAEWOULDBLOCK/E WOULDBLOCK。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM