最近在網路上看到一些文章以及回複,同時又搜尋了一些網路上關於阻塞非阻塞區別的描述,發現很多人在描述兩者的發送接收時操作返回以及緩衝區處理的區別時有不同程度的誤解。所以我想寫一篇文章來糾正錯誤,並作為記錄方便查閱,如有轉載,註明作者(jwybobo2007)以及出處即可。
首先socket在預設情況下是阻塞狀態的(未指非同步作業以及其它一些特殊用途下,直接預設為非阻塞),這就使得發送以及接收操作處於阻塞的狀態,即調用不會立即返回,而是進入睡眠等待操作完成。下面把討論點分為發送以及接收。
一.發送選用send(這裡特指TCP)以及sendto(這裡特指UDP)來描述
首先需要說明的是,不管阻塞還是非阻塞,在發送時都會將資料從應用緩衝區拷貝到核心緩衝區(SO_RCVBUF選項聲明,除非緩衝區大小為0)。我在網路上看到某些人說,阻塞就是將資料真正發送給對方,並且阻塞是發生在需要把前面的所有資料全部發送出去,然後再發送本次的,而非阻塞則是拷貝到發送緩衝區。我不得不說,上面的這種說法是錯誤的。
在阻塞模式下send操作將會等待所有資料均被拷貝到發送緩衝區後才會返回。
如果當前發送緩衝總大小為8192,已經拷貝到緩衝的資料為8000,那剩餘的大小為192,現在需要發送2000位元組資料,那阻塞發送就會等待緩衝區足夠把所有2000位元組資料拷貝進去,如第一次拷貝進192位元組,當緩衝區成功發送出1808位元組後,再把應用緩衝區剩餘的1808位元組拷貝到核心緩衝,而後send操作返回成功發送位元組數。
從上面的過程不難看出,阻塞的send操作返回的發送大小,必然是你參數中的發送長度的大小。
在阻塞模式下的sendto操作不會阻塞。
關於這一點的原因在於:UDP並沒有真正的發送緩衝區,它所做的只是將應用緩衝區拷貝給下層協議棧,在此過程中加上UDP頭,IP頭,所以實際不存在阻塞。
在非阻塞模式下send操作調用會立即返回。
關於立即返回大家都不會有異議。還是拿阻塞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
原文地址:http://blog.csdn.net/jwybobo2007/archive/2011/01/26/6164362.aspx
評論:
牛逼,尤其是對send的阻塞說明,可能很多人都理解錯了,有沒有一種方法,可以達到下面的這種效果呢。
阻塞就是將資料真正發送給對方,並且阻塞是發生在需要把前面的所有資料全部發送出去,然後再發送本次的 Re: jwybobo2007 2013-07-17 14:23發表 [回複] [引用] [舉報] 回複xiaoxiaozhu2010:如果你想要阻塞時不進緩衝區而直接傳資料的話,可以設定緩衝區為0
如果你想測試send會出現阻塞的這種情況可以這樣做:
server端將緩衝區設定的小一些,而後listen的socket不進行recv操作;client端不停的send,應該很快會出現send被阻塞的情況