socket編程讀寫文件recv()、send()函數返回值分析

<1>recv方法:

模型:

 #include <sys/types.h>

 #include <sys/socket.h>

 ssize_t recv(int sockfd, void *buf, size_t len, int flags);

參數:

sockfd創建的文件描述符fd,buf接收數據的緩衝區,len接收數據的長度,flags表示信息,默認設置爲0

當應用程序調用recv接收數據的時候,recv函數會等待sockfd中發送數據的緩衝區的協議發送完數據,如果在等待過程中出現網絡錯誤,則會返回SOCKET_ERROR。如果sockfd中的緩衝區中沒有數據或者協議已經發送完數據,則recv會檢查sockfd的接受緩衝區,如果該緩衝區正在接受數據,則recv會一直等待,直到緩衝區接受數據完畢,之後recv將數據從緩衝區拷貝一份值buf中,數據通過協議轉發的,recv只是將數據從緩衝區拷貝過來。注,如果recv在拷貝數據時出現錯誤,則返回SOCKET_ERROR,如果在協議傳輸數據中出現網絡錯誤,則返回0。

阻塞與非阻塞recv返回值沒有區別,都是:

       <0 出錯    

       =0 對方調用了close API來關閉連接

       >0 接收到的數據大小,

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

但是如下特點:


只是阻塞模式下recv會一直阻塞直到接收到數據,非阻塞模式下如果沒有數據就會返回,不會阻塞着讀,因此需要循環讀取)。

返回說明:   

(1)成功執行時,返回接收到的字節數。

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

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

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

EBADF:sock不是有效的描述詞

ECONNREFUSE:遠程主機阻絕網絡連接

EFAULT:內存空間訪問出錯

EINTR:操作被信號中斷

EINVAL:參數無效

ENOMEM:內存不足

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

ENOTSOCK:sock索引的不是套接字

<2>send方法:

模型:

#include <sys/types.h>
#include <sys/socket.h>

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

參數:

sockfd:創建的sockfd文件描述符,buf發送數據所在的數據區,len發送數據的長度,flags標誌位默認設置爲0

       當程序使用send方法的時候,send會首先檢查協議sockfd中的發送緩衝區中是否有數據發送,send會比較發送數據的buf長度和sockfd發送數據的緩衝區長度,如果len大於sockfd的發送長度,則send返回SOCKET_ERROR,如果發送緩衝區的大小足夠,則將數據buf中的數據發送至發送緩衝區中,確認send函數將數據拷貝至發送緩衝區中,另外,如果send檢測發送緩衝區有數據但是還未發送,就比較該緩衝區的剩餘空間和和len的大小,如果len大於剩餘空間,就一直等待,直到緩衝區中的數據發送完爲止,如果len<緩衝區剩餘的大小,就將發送的數據拷貝至該緩衝區中,如果send函數copy數據成功,就返回實際copy的字節數,如果send在copy數據時出現錯誤,那麼send就返回SOCKET_ERROR;如果send在等待協議傳送數據時網絡斷開的話,那麼send函數也返回SOCKET_ERROR。(send函數只是將數據拷貝至發送緩衝中,就返回,此刻數據不一定發送至接收端)。

總結,不管是send還是recv方法,都是數據的緩衝區和發送的緩衝區的拷貝操作過程,真正發送數據的是協議功能,注意三種返回值的可能性,>0表示成功,返回實際發送或接受的字節數,=0表示超時,對方主動關閉了連接過程,<0出錯,此種情況可能出現過重情況,如上所示。其中errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN這三種是特殊情況,實際使用中表示繼續正常接受數據即可。

如圖所示通信過程:

<3>send()方法的行爲

    對於send方法,將需要發送的數據拷貝至發送緩衝區,否則進入阻塞或者進入超時等待。如果改變這種狀態,將發送緩衝區大小設置爲0,這樣,當send方法返回是,所發送的數據就都到達目的機器。但是,只是到達目標服務器的接受緩衝區,並不保證數據以被應用層所接收。另外, 在發送數據時,協議根據滑動窗口和MSS值來確定tcp報文段的數據字段大小,這樣就能保證接收緩衝區不會溢出。如果接收方的滑動窗口爲0,但是發送方還有數據尚未發送完成,就是用探測機制,一方面檢測對方方的滑動窗口的大小變化(探測機制是通過每次發送一個字節來進行檢測,由先前的30s到之後的1分鐘,最終達到2分鐘間隔)),另一方面檢測對方的連接是否異常。

     push標誌指示接收端應儘快將數據提交給應用層。如果send函數提交的待發送數據量較小,例如小於1460B(參照MSS值確定),那麼協議層會將該報文中的TCP頭部的push字段置爲1;如果待發送的數據量較大,需要拆成多個數據段發送時,協議層只會將最後一個分段報文的TCP頭部的push字段置1。

<4>recv()方法的行爲

    對recv方法來說,將接收緩衝區中的數據拷貝至應用層的緩衝區中,當應用緩衝區滿或者接受緩衝區數據接收完,就會返回。如果將接受緩衝區大小設置爲0,那麼該方法會直接從協議中的滑動窗口中獲取數據。要麼緩衝區接收滿爲止。要麼當push標誌位1的時候 ,recv返回實際接收的數據大小。

協議層收到TCP數據包後(保存在滑動窗口區),本方的滑動窗口合攏(窗口值減小);當協議層將數據拷貝到接收緩衝區(滑動窗口區—>接收緩衝區),或者應用層調用recv接收數據(接收緩衝區—>應用層緩衝區,滑動窗口區—>應用層緩衝區)後,本方的滑動窗口張開(窗口值增大)。收到數據更新window後,協議層向對方發送ACK確認。
————————————————
版權聲明:本文爲CSDN博主「一顆簡單的心」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_26105397/article/details/80988429

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章