一、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;//跳出接收循環
}
}