TCP send 與 recv

一、send

如果是阻塞的IO,首先要填滿buffer纔會發送出去,否則阻塞

1)檢查用戶的buffer和socket的buffer大小,如果用戶buffer大於socket 的buffer則返回錯誤

2) 如果socket的buffer大於等於用戶的buffer,則準備返送

3)send先檢查協議是否正在發送s的發送緩衝中的數據,如果是就等待協議把數據發送完。

如果協議還沒有開始發送s的發送緩衝中的數據或者s的發送緩衝中沒有數據,那麼send就比較s的發送緩衝區的剩餘空間和len 

4)如果剩餘空間小於用戶buffer,則等待協議將s緩衝區中的數據發送完畢

5)如果剩餘空間大於用於buffer,則將用戶buffer 中的數據copy到緩衝區中。

(注意並不是send把s的發送緩衝中的數據傳到連接的另一端的,而是協議傳的send僅僅是把buf中的數據copy到s的發送緩衝區的剩餘空間裏)。 

while()
{
    int ret = send(sock, buf, len, 0);
    if(ret>0 && ret < len)
    {
        continue;
    }
    else if(ret == 0)
    {
        //關閉連接
        break;
    }
    else 
    {
       if(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)
           continue;
       else
           break;
    }   
}

二、recv

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

recv只是負責從接收緩衝區中copy數據到用戶空間。真正接收數據的工作是由協議完成的。

不論是客戶還是服務器應用程序都用recv函數從TCP連接的另一端接收數據。

這裏只描述同步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信號,進程對該信號的默認處理是進程終止。 

 

非阻塞模式下的接收
while(rs) 
{ 
    buflen = recv(activeevents[i].data.fd,buf,sizeof(buf),0)
    if(buflen > 0) 
    {  
        rs=1
    }
    else if(buflen==0) //對端關閉,接收完畢
    { 
        break;
    } 
    //EINTR,被信號終端,EWOULDBLOCK暫時沒有數據,
    //EAGAIN這表明你在非阻塞模式下調用了阻塞操作,在該操作沒有完成就返回這個錯誤,
    //這個錯誤不會破壞socket的同步,不用管它,下次循環接着recv就可以。
    else if(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) 
        continue;
} 
阻塞模式下的接收
while(rs) 
{ 
    buflen = recv(activeevents[i].data.fd,buf,sizeof(buf),0) 
    if(buflen > 0) 
    { 
       //正常處理
    }
    else if(buflen == 0) 
    { 
         //這裏表示對端的socket已正常關閉. 
         if(buflen==sizeof(buf) rs=1//需要再次讀取 
         else rs=0 //sizeof(buf)<buflen, 證明數據接收完畢
    } 
    else
    {
        if(errno == EAGAIN||errno == EINTR)
        {
            continue;//繼續接收數據
        }
        break;//跳出接收循環
    }
} 

 

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