recv send 阻塞和非阻塞

来源:互联网 发布:淘宝客优惠券网站模板 编辑:程序博客网 时间:2024/04/30 17:01

        转自:http://blog.csdn.net/xiaofei0859/article/details/6037814/

        在阻塞模式下, send函数的过程是将应用程序请求发送的数据拷贝到发送缓存中发送就返回.但由于发送缓存的存在,表现为:如果发送缓存大小比请求发送的大小要大,那么send函数立即返回,同时向网络中发送数据;否则,send会等待接收端对之前发送数据的确认,以便腾出缓存空间容纳新的待发送数据,再返回(接收端协议栈只要将数据收到接收缓存中,就会确认,并不一定要等待应用程序调用recv),如果一直没有空间能容纳待发送的数据,则一直阻塞;

        在非阻塞模式下,send函数的过程仅仅是将数据拷贝到协议栈的缓存区而已,如果缓存区可用空间不够,则尽能力的拷贝,立即返回成功拷贝的大小;如缓存区可用空间为0,则返回-1,同时设置errno为EAGAIN.


         阻塞就是干不完不准回来,非阻塞就是你先干,我现看看有其他事没有,完了告诉我一声。

         我们拿最常用的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函数的接受buffer大小设置为0时,recv函数返回0,errno为0

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 苹果手机下不了软件怎么办 苹果6s下不了软件怎么办 苹果6下不了软件怎么办 ipad更新系统卡住了怎么办 ipad卡住了关不了机怎么办 ipad卡住了没反应怎么办 苹果7下不了软件怎么办 苹果手机开机密码忘了怎么办 苹果开机密码忘了怎么办 我的ipad很卡怎么办 6s升级后卡顿严重怎么办 软件升级后手机卡顿怎么办 ps通道抠图模糊怎么办 电脑锁屏死机了怎么办 ipad电源键坏了怎么办 华为平板死机黑屏了怎么办 苹果4s锁屏键坏了怎么办 平板电脑密码锁忘记密码怎么办 苹果平板电脑密码锁忘记密码怎么办 平板电脑密码锁密码锁死怎么办 小米平板黑屏按键亮怎么办 小米平板黑屏没反应怎么办 小米平板一刷黑屏了怎么办 小米4卡死了怎么办啊 苹果平板电脑开不了机怎么办 ipad开不了机怎么办都是黑屏 手机突然黑屏了死机状态怎么办 苹果平板死机不能重启怎么办 韩众平板死机了怎么办 苹果平板输入密码多次停用怎么办 ld密码被停用了怎么办 档案被自己丢了怎么办 手机在厂里丢了怎么办 在厂里借工具丢了怎么办 导出的考勤没有姓名怎么办 退休时档案丢了怎么办 职工与企业没有劳资怎么办 去大学报道的档案袋丢失怎么办 档案入学毕业年份写错怎么办 从事业单位辞职后人事档案怎么办 老师辞职不给批怎么办