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

 

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