奇怪的recv函式

2021-06-22 08:35:57 字數 2816 閱讀 5074

一直有個錯覺,以為recv的函式返回值》0是有資料讀到,=0是無資料,<0是連線關閉等錯誤。結果最近做了個server,發現檢測不到對端連線關閉,才知道犯了個天大的錯誤,而原因就是recv的返回值的怪異。

從下文才知道,即使前面select read控制代碼返回大於0,recv函式返回0竟然是代表連線關閉。而且recv返回小於0也不一定是出錯,而根據errno判斷還有可能只是沒有讀到資料~!!!!

1、阻塞模式與非阻塞模式下recv的返回值各代表什麼意思?有沒有區別?(就 我目前了解阻塞與非阻塞recv返回值沒有區分,都是 <0:出錯,=0:連線關閉,>0接收到資料大小,特別:返回值 <0時並且(errno == eintr || errno == ewouldblock || errno == eagain)的情況下認為連線是正常的,繼續接收。只是阻塞模式下recv會阻塞著接收資料,非阻塞模式下如果沒有資料會返回,不會阻塞著讀,因此需要迴圈讀取)。

2、阻塞模式與非阻塞模式下write的返回值各代表什麼意思?有沒有區別?(就我目前了解阻塞與非阻塞write返回值沒有區分,都是 <0:出錯,=0:連線關閉,>0傳送資料大小,特別:返回值 <0時並且(errno == eintr || errno == ewouldblock || errno == eagain)的情況下認為連線是正常的,繼續傳送。只是阻塞模式下write會阻塞著傳送資料,非阻塞模式下如果暫時無法傳送資料會返回,不會阻塞著 write,因此需要迴圈傳送)。

3、阻塞模式下read返回值 < 0 && errno != eintr && errno != ewouldblock && errno != eagain時,連線異常,需要關閉,read返回值 < 0 && (errno == eintr || errno == ewouldblock || errno == eagain)時表示沒有資料,需要繼續接收,如果返回值大於0表示接送到資料。

非阻塞模式下read返回值 < 0表示沒有資料,= 0表示連線斷開,> 0表示接收到資料。

這2種模式下的返回值是不是這麼理解,有沒有跟詳細的理解或跟準確的說明?

4、阻塞模式與非阻塞模式下是否send返回值 < 0 && (errno == eintr || errno == ewouldblock || errno == eagain)表示暫時傳送失敗,需要重試,如果send返回值 <= 0, && errno != eintr && errno != ewouldblock && errno != eagain時,連線異常,需要關閉,如果send返回值 > 0則表示傳送了資料?send的返回值是否這麼理解,阻塞模式與非阻塞模式下send返回值=0是否都是傳送失敗,還是那個模式下表示暫時不可傳送,需要重發?

5、很多人說阻塞模式下read會阻塞著讀,是否這樣?我和同事試了不會阻塞read。

6、網路上找了很多資料,說的都很籠統,就分大於0,小於0,等於0,並沒有區分阻塞與非阻塞,更沒有區分乙個錯誤號,希望哪位高手能按上面的問題逐條回答一下,越詳細越好,平時少上csdn,分少,見諒。

select():

select()的機制中提供一fd_set的資料結構,實際上是一long型別的陣列,每乙個陣列元素都能與一開啟的

檔案控制代碼(不管是socket控制代碼,還是其他 檔案或命名管道或裝置控制代碼)建立聯絡,建立聯絡的工作由程式設計師

完成,當呼叫select()時,由核心根據io狀態修改fd_set的內容,由此來通知執行了select()的程序哪一

socket或檔案可讀,下面具體解釋:

int select(nfds, readfds, writefds, exceptfds, timeout)

int nfds;

fd_set *readfds, *writefds, *exceptfds;

struct tim *timeout;

ndfs:select監視的檔案控制代碼數,視程序中開啟的檔案數而定,一般設為你要監視各檔案中的最大檔案號

加一。readfds:select監視的可讀檔案控制代碼集合。

writefds: select監視的可寫檔案控制代碼集合。

exceptfds:select監視的異常檔案控制代碼集合

timeout:本次select()的超時結束時間。(見/usr/include/sys/select.h,可精確至百萬分之一

秒!)當readfds或writefds中映象的檔案可讀或可寫或超時,本次select() 就結束返回。程式設計師利用一組系

統提供的巨集在select()結束時便可判 斷哪一檔案可讀或可寫。對socket程式設計特別有用的就是readfds。

相關的巨集解釋如下:

fd_zero(fd_set *fdset):清空fdset與所有檔案控制代碼的聯絡。

fd_set(int fd, fd_set *fdset):建立檔案控制代碼fd與fdset的聯絡。

fd_clr(int fd, fd_set *fdset):清除檔案控制代碼fd與fdset的聯絡。

fd_isset(int fd, fdset *fdset):檢查fdset聯絡的檔案控制代碼fd是否可讀寫,>0表示可讀寫。

(關於fd_set及相關巨集的定義見/usr/include/sys/types.h)

這樣,你的socket只需在有東東讀的時候才讀入,大致如下:

...int sockfd;

fd_set fdr;

struct tim timeout = ..;

...for(;;)  }}

所以乙個fd_isset(sockfd)就相當通知了sockfd可讀。

至於struct tim在此的功能,請man select。不同的tim設定使使select()表現出超時結束、

無超時阻塞和輪詢三種特性。由於tim可精確至百萬分之一秒,所以windows的settimer()根本不算什麼。你可以用select()做乙個超級時鐘

recv函式解析

recv函式 i nt recv socket s,cha r buf,int len,int flags 不論是客戶還是伺服器應用程式都用recv函式從tcp連線的另一端接收資料。該函式的第乙個引數指定接收端套接字描述符 第二個引數指明乙個緩衝區,該緩衝區用來存放recv函式接收到的資料 第三個引...

recv函式的用法詳解

recv函式 int recv socket s,char far buf,int len,int flags 不論是客戶還是伺服器應用程式都用recv函式從tcp連線的另一端接收資料。該函式的第乙個引數指定接收端套接字描述符 第二個引數指明乙個緩衝區,該緩衝區用來存放recv函式接收到的資料 第三...

Socket send函式和recv函式

不論是客戶還是伺服器應用程式都用send函式來向tcp連線的另一端傳送資料。客戶程式一般用send函式向伺服器傳送請求,而伺服器則通常用send函式來向客戶程式傳送應答。該函式的第乙個引數指定傳送端套接字描述符 第二個引數指明乙個存放應用程式要傳送資料的緩衝區 第三個引數指明實際要傳送的資料的位元組...