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