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。
轉載請註明作者:jwybobo2007