日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

recv send 阻塞和非阻塞

發布時間:2024/4/11 编程问答 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 recv send 阻塞和非阻塞 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

int send( SOCKET s, const char FAR *buf, int len, int flags );

不論是客戶還是服務器應用程序都用send函數來向TCP連接的另一端發送數據。客戶程序一般用send函數向服務器發送請求,而服務器則通常用send函數來向客戶程序發送應答。

?

該函數的第一個參數指定發送端套接字描述符;

?

第二個參數指明一個存放應用程序要發送數據的緩沖區;

?

第三個參數指明實際要發送的數據的字節數;

?

第四個參數一般置0。

?

這里只描述同步Socket的send函數的執行流程。當調用該函數時,

?

(1)send先比較待發送數據的長度len和套接字s的發送緩沖的長度, 如果len大于s的發送緩沖區的長度,該函數返回SOCKET_ERROR;

(2)如果len小于或者等于s的發送緩沖區的長度,那么send先檢查協議是否正在發送s的發送緩沖中的數據,如果是就等待協議把數據發送完,如果協議 還沒有開始發送s的發送緩沖中的數據或者s的發送緩沖中沒有數據,那么send就比較s的發送緩沖區的剩余空間和len

(3)如果len大于剩余空間大小,send就一直等待協議把s的發送緩沖中的數據發送完

(4)如果len小于剩余 空間大小,send就僅僅把buf中的數據copy到剩余空間里(注意并不是send把s的發送緩沖中的數據傳到連接的另一端的,而是協議傳的,send僅僅是把buf中的數據copy到s的發送緩沖區的剩余空間里)。

?

?

如果send函數copy數據成功,就返回實際copy的字節數,如果send在copy數據時出現錯誤,那么send就返回SOCKET_ERROR;如果send在等待協議傳送數據時網絡斷開的話,那么send函數也返回SOCKET_ERROR。

?

要注意send函數把buf中的數據成功copy到s的發送緩沖的剩余空間里后它就返回了,但是此時這些數據并不一定馬上被傳到連接的另一端。如 果協議在后續的傳送過程中出現網絡錯誤的話,那么下一個Socket函數就會返回SOCKET_ERROR。(每一個除send外的Socket函數在執 行的最開始總要先等待套接字的發送緩沖中的數據被協議傳送完畢才能繼續,如果在等待時出現網絡錯誤,那么該Socket函數就返回 SOCKET_ERROR)

?

注意:在Unix系統下,如果send在等待協議傳送數據時網絡斷開的話,調用send的進程會接收到一個SIGPIPE信號,進程對該信號的默認處理是進程終止。

?

通過測試發現,異步socket的send函數在網絡剛剛斷開時還能發送返回相應的字節數,同時使用select檢測也是可寫的,但是過幾秒鐘之后,再send就會出錯了,返回-1。select也不能檢測出可寫了。

?

?

?

2. recv函數

int recv( SOCKET s, char FAR *buf, int len, int flags);

?

不論是客戶還是服務器應用程序都用recv函數從TCP連接的另一端接收數據。該函數的第一個參數指定接收端套接字描述符;

?

第二個參數指明一個緩沖區,該緩沖區用來存放recv函數接收到的數據;

?

第三個參數指明buf的長度;

?

第四個參數一般置0。

?

這里只描述同步Socket的recv函數的執行流程。當應用程序調用recv函數時,

?

(1)recv先等待s的發送緩沖中的數據被協議傳送完畢,如果協議在傳送s的發送緩沖中的數據時出現網絡錯誤,那么recv函數返回SOCKET_ERROR,

?

(2)如果s的發送緩沖中沒有數據或者數據被協議成功發送完畢后,recv先檢查套接字s的接收緩沖區,如果s接收緩沖區中沒有數據或者協議正在接收數 據,那么recv就一直等待,直到協議把數據接收完畢。當協議把數據接收完畢,recv函數就把s的接收緩沖中的數據copy到buf中(注意協議接收到的數據可能大于buf的長度,所以 在這種情況下要調用幾次recv函數才能把s的接收緩沖中的數據copy完。recv函數僅僅是copy數據,真正的接收數據是協議來完成的),

?

recv函數返回其實際copy的字節數。如果recv在copy時出錯,那么它返回SOCKET_ERROR;如果recv函數在等待協議接收數據時網絡中斷了,那么它返回0。

?

注意:在Unix系統下,如果recv函數在等待協議接收數據時網絡斷開了,那么調用recv的進程會接收到一個SIGPIPE信號,進程對該信號的默認處理是進程終止。

阻塞就是干不完不準回來,???
非組賽就是你先干,我現看看有其他事沒有,完了告訴我一聲

我們拿最常用的send和recv兩個函數來說吧...?
比如你調用send函數發送一定的Byte,在系統內部send做的工作其實只是把數據傳輸(Copy)到TCP/IP協議棧的輸出緩沖區,它執行成功并不代表數據已經成功的發送出去了,如果TCP/IP協議棧沒有足夠的可用緩沖區來保存你Copy過來的數據的話...這時候就體現出阻塞和非阻塞的不同之處了:對于阻塞模式的socket send函數將不返回直到系統緩沖區有足夠的空間把你要發送的數據Copy過去以后才返回,而對于非阻塞的socket來說send會立即返回WSAEWOULDDBLOCK告訴調用者說:"發送操作被阻塞了!!!你想辦法處理吧..."?
對于recv函數,同樣道理,該函數的內部工作機制其實是在等待TCP/IP協議棧的接收緩沖區通知它說:嗨,你的數據來了.對于阻塞模式的socket來說如果TCP/IP協議棧的接收緩沖區沒有通知一個結果給它它就一直不返回:耗費著系統資源....對于非阻塞模式的socket該函數會馬上返回,然后告訴你:WSAEWOULDDBLOCK---"現在沒有數據,回頭在來看看"

?

?

讀數據的時候需要考慮的是當recv()返回的大小如果等于請求的大小,那么很有可能是緩沖區還有數據未讀完,也意味著該次事件還沒有處理完,所以還需要再次讀取
while(rs)
{
buflen = recv(activeevents[i].data.fd, buf, sizeof(buf), 0);
if(buflen < 0)
{
??? // 由于是非阻塞的模式,所以當errno為EAGAIN時,表示當前緩沖區已無數據可讀
??? // 在這里就當作是該次事件已處理處.
??? if(errno == EAGAIN)
???? break;
??? else
???? return;
?? }
?? else if(buflen == 0)
?? {
???? // 這里表示對端的socket已正常關閉.
?? }
?? if(buflen == sizeof(buf)
???? rs = 1;?? //?
需要再次讀取
?? else
???? rs = 0;
}


還有,假如發送端流量大于接收端的流量(意思是epoll所在的程序讀比轉發的socket要快),由于是非阻塞的socket,那么send()函數雖然返回,但實際緩沖區的數據并未真正發給接收端,這樣不斷的讀和發,當緩沖區滿后會產生EAGAIN錯誤(參考man send),同時,不理會這次請求發送的數據.所以,需要封裝socket_send()的函數用來處理這種情況,該函數會盡量將數據寫完再返回,返回-1表示出錯。在socket_send()內部,當寫緩沖已滿(send()返回-1,errnoEAGAIN),那么會等待后再重試.這種方式并不很完美,在理論上可能會長時間的阻塞在socket_send()內部,但暫沒有更好的辦法.

ssize_t socket_send(int sockfd, const char* buffer, size_t buflen)
{
ssize_t tmp;
size_t total = buflen;
const char *p = buffer;

while(1)
{
??? tmp = send(sockfd, p, total, 0);
??? if(tmp < 0)
??? {
????? // 當send收到信號時,可以繼續寫,但這里返回-1.
????? if(errno == EINTR)
??????? return -1;

????? // 當socket是非阻塞時,如返回此錯誤,表示寫緩沖隊列已滿,
????? // 在這里做延時后再重試.
????? if(errno == EAGAIN)
????? {
??????? usleep(1000);
??????? continue;
????? }

????? return -1;
??? }

??? if((size_t)tmp == total)
????? return buflen;

??? total -= tmp;
??? p += tmp;
}

return tmp;
}

總結

以上是生活随笔為你收集整理的recv send 阻塞和非阻塞的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。