1. 程式人生 > >send/recv與socket

send/recv與socket

send函式

不論是客戶端還是伺服器端應用程式都用send函式來向TCP連線的另一端傳送資料。客戶端程式一般用send函式向伺服器傳送請求,而伺服器則通常用send函式來向客戶程式傳送應答。

當呼叫send函式時,send先比較待發送資料的長度len和套接字s的傳送緩衝區,如果len大於傳送緩衝區的長度,該函式返回錯誤SOCKET_ERROR;如果len <= s的傳送緩衝區,那麼send先檢查協議是否正在傳送s的傳送緩衝區中的資料,如果是就等待協議把資料傳送完畢,如果協議還沒有開始傳送s的傳送緩衝區中的資料或者s的傳送緩衝區中沒有資料,那麼send就比較s的傳送緩衝區的剩餘空間和len

,如果len > 剩餘空間 send就一直等待協議把s的傳送緩衝區中的資料傳送完,如果len < 剩餘空間 send就僅僅把buf中的資料copy到剩餘空間裡(send僅僅是把buf中的資料copy到s的傳送緩衝區中,至於把資料傳送到另一端是協議傳的不是send)。如果send函式copy資料成功,就返回實際copy的位元組數,如果send在copy資料時出現錯誤或者在等待協議傳送資料時網路斷開,那麼send函式也返回SOCKET_ERROR。

要注意send函式把buf中的資料成功copy到s的傳送緩衝區的剩餘空間裡,send函式就返回了,但是此時這些資料並不一定馬上被傳送到連線的另一端。如果協議在後續的傳送過程中出現網路錯誤的話,那麼下一個socket函式就會返回SOCKET_ERROR。但是注意的是:每一個除send以外的socket函式在執行的最開始總要等待套接字的傳送緩衝區中的資料被協議傳送完畢才能繼續,如果在等待時出現網路錯誤,那麼該socket函式就返回SOCKET_ERROR。

注意:在Unix系統之下,如果send在等待協議傳送資料時網路斷開,呼叫send的程序會接受到一個SIGPIPE訊號,程序對該訊號的預設處理是程序終止。

錯誤程式碼:

EBADF 引數s 非合法的socket處理程式碼。
EFAULT 引數中有一指標指向無法存取的記憶體空間
ENOTSOCK 引數s為一檔案描述詞,非socket。
EINTR 被訊號所中斷。
EAGAIN 此操作會令程序阻斷,但引數s的socket為不可阻斷。
ENOBUFS 系統的緩衝記憶體不足
ENOMEM 核心記憶體不足
EINVAL 傳給系統呼叫的引數不正確。

recv函式

當呼叫recv函式時,recv函式先等待s的傳送緩衝區中的資料都被協議傳送完畢,如果協議在傳送資料時出現了網路錯誤,那麼recv函式返回SOCKET_ERROR,如果s的傳送緩衝區中沒有資料或者協議成功傳送完畢後,recv函式先檢查s的接受緩衝區,如果s接收緩衝區中沒有資料或者正在接收資料,那麼recv函式久一直等待直到協議把資料接收完畢。當協議把資料接收完畢,recv函式就把s的接收緩衝區中的資料copy到buf中(注意協議接收到的資料可能大於buf的長度,所以在這種情況下要呼叫幾次recv函式才能把s的接收緩衝區中的資料copy完畢。recv函式僅僅是拷貝資料,真正的接收資料是協議來完成的),recv函式返回其實際copy的位元組數。如果recv函式在copy時出錯,返回SOCKET_ERROR,如果recv函式在等待協議接收資料時網路中斷了,那麼recv函式返回0。

注意:在Unix系統下,如果recv函式在等待協議接收資料時網路斷開了,那麼呼叫recv的程序會接收到一個SIGPIPE訊號,程序對該訊號的預設處理是程序終止。

返回值說明

阻塞模式下recv會一直阻塞直到接收到資料,非阻塞模式下如果沒有資料就會返回,不會阻塞著讀,因此需要迴圈讀取)。

返回說明:   

(1)成功執行時,返回接收到的位元組數。

(2)若另一端已關閉連線則返回0,這種關閉是對方主動且正常的關閉

(3)失敗返回-1,errno被設為以下的某個值   

特別地:返回值<0時並且(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)的情況下認為連線是正常的,繼續接收。

EAGAIN:套接字已標記為非阻塞,而接收操作被阻塞或者接收超時

EBADF:sock不是有效的描述詞

ECONNREFUSE:遠端主機阻絕網路連線

EFAULT:記憶體空間訪問出錯

EINTR:操作被訊號中斷

EINVAL:引數無效

ENOMEM:記憶體不足

ENOTCONN:與面向連線關聯的套接字尚未被連線上

ENOTSOCK:sock索引的不是套接字

socket緩衝區

預設情況下socket是阻塞的。

send函式並不是直接將資料傳輸到網路中,而是負責將資料寫入輸出緩衝區,資料從輸出緩衝區傳送到目標主機是由TCP協議完成的。資料寫入到輸出緩衝區之後,send函式就可以返回了,資料是否傳送出去,是否傳送成功,何時到達目標主機,都不由它負責了,而是由協議負責。

recv函式也是一樣的,它並不是直接從網路中獲取資料,而是從輸入緩衝區中讀取資料。

輸入輸出緩衝區,系統會為每個socket都單獨分配,並且是在socket建立的時候自動生成的。一般來說,預設的輸入輸出緩衝區大小為8K。套接字關閉的時候,輸出緩衝區的資料不會丟失,會由協議傳送到另一方;而輸入緩衝區的資料則會丟失。

Socket資料傳送與接收問題

資料的傳送和接收是獨立的,並不是傳送方執行一次send,接收方就執行以此recv。recv函式不管傳送幾次,都會從輸入緩衝區儘可能多的獲取資料。如果傳送方傳送了多次資訊,接收方沒來得及進行recv,則資料堆積在輸入緩衝區中,取資料的時候會都取出來。換句話說,recv並不能判斷資料包的結束位置。

send函式:
在資料進行傳送的時候,需要先檢查輸出緩衝區的可用空間大小,如果可用空間大小小於要傳送的資料長度,則send會被阻塞,直到緩衝區中的資料被髮送到目標主機,有了足夠的空間之後,send函式才會將資料寫入輸出緩衝區。

TCP協議正在將資料傳送到網路上的時候,輸出緩衝區會被鎖定(生產者消費者問題),不允許寫入,send函式會被阻塞,直到資料傳送完,輸出緩衝區解鎖,此時send才能將資料寫入到輸出緩衝區

要寫入的資料大於輸出緩衝區的最大長度的時候,要分多次寫入,直到所有資料都被寫到緩衝區之後,send函式才會返回。

recv函式:
函式先檢查輸入緩衝區,如果輸入緩衝區中有資料,讀取出緩衝區中的資料,否則的話,recv函式會被阻塞,等待網路上傳來資料。如果讀取的資料長度小於輸出緩衝區中的資料長度,沒法一次性將所有資料讀出來,需要多次執行recv函式,才能將資料讀取完畢。

 

參考:

https://blog.csdn.net/u010270148/article/details/53605339

https://www.cnblogs.com/sunziying/p/6501045.html

https://www.cnblogs.com/Berryxiong/p/6547510.html

https://www.cnblogs.com/msb-/articles/6042413.html

https://blog.csdn.net/u010871058/article/details/76147082