linux下使用write\send发送数据报 EAGAIN : Resource temporarily unavailable 错

首先是我把套接字设置为异步的了,然后在使用write发送数据时采取的方式是循环发送大量的数据;由于是异步的,write\send将要发送的
数据提交到发送缓冲区后是立即返回的,并不需要对端确认数据已接收。在这种情况下是很有可能出现发送缓冲区被填满,导致write\send无法再向缓冲
区提交要发送的数据。因此就产生了Resource temporarily unavailable的错误,EAGAIN
的意思也很明显,就是要你再次尝试。

把发送部分修改如下

  1. int SeanSend(int fd, void *buffer, int length)
  2. {
  3. int bytes_left;
  4. int written_bytes;
  5. char *ptr;
  6. ptr=(char *)buffer;
  7. bytes_left=length;
  8. while(bytes_left>0)
  9. {
  10. /* 开始写*/
  11. written_bytes=write(fd, ptr, bytes_left);
  12. if(written_bytes<=0) /* 出错了*/
  13. {
  14. if(errno==EINTR) /* 中断错误 我们继续写*/
  15. {
  16. continue;
  17. printf("[SeanSend]error errno==EINTR continue\n");
  18. }
  19. else if(errno==EAGAIN) /* EAGAIN : Resource temporarily unavailable*/
  20. {
  21. sleep(1);//等待一秒,希望发送缓冲区能得到释放
  22. continue;
  23. printf("[SeanSend]error errno==EAGAIN continue\n");
  24. }
  25. else /* 其他错误 没有办法,只好退了*/
  26. {
  27. printf("[SeanSend]ERROR: errno = %d, strerror = %s \n"
  28. , errno, strerror(errno));
  29. return(-1);
  30. }
  31. }
  32. bytes_left-=written_bytes;
  33. ptr+=written_bytes;/* 从剩下的地方继续写?? */
  34. }
  35. return length;
  36. }

非阻塞socket编程问题小结

http://blog.sina.com.cn/s/blog_4462f8560100tvu4.html

项目需要写一个主动连接且定时发送数据的客户端程序,并保证传输数据的可靠性和稳定性。

注意的问题有:

1.connect返回值判定

之前的程序

if(connect(tcp_client_sock,(structsockaddr*)&server, server_length) <0)

//向服务器发起连接,连接成功后client_socket代表了客户机和服务器的一个socket连接
     {
       printf("Can Not Connect To %s!\n",SERVE_IP);
       close(tcp_client_sock);

return-1;
       //exit(1);
     }
     else
     {
     TCP_CONNET_FLAG = 1;//建立连接

...

}

但是在网上查询发现:当我们以非阻塞的方式来进行连接的时候,返回的结果如果是-1,这并不代表
这次连接发生了错误,如果它的返回结果是EINPROGRESS,那么就代表连接还在进行中。后面可以通过poll或者select来判断socket是
否可写,如果可以写,说明连接完成了。

更改如下:

//先定位为非阻塞模式,立即返回状态;如有错误存为SO_ERROR值

if((flags =fcntl(tcp_client_sock,F_GETFL, 0 )) < 0)
        {
       perror("fcntl");
       return -1;
        }
        flags |= O_NONBLOCK;
        if(fcntl(tcp_client_sock, F_SETFL, flags) < 0)//设置socket为非阻塞模式
       {
         perror("fcntl");
         return -1;
       }

if(connect(tcp_client_sock,(structsockaddr*)&server, server_length) <0) //向服务器发起连接
       {

if(errno != EINPROGRESS)//非等待状态
           {
             perror("connect error");

close(tcp_client_sock);//下一步重连

//return -1;
           }
           else//EINPROGRESS:正常处理连接
           {

perror("connect");//查询ERROR值
              printf("check delay connected\n");
              goto done;//进一步检查是否握手完成
           }
       }

else
       {
    done:

...//下一步的select判定connect是否完成及tcp_client_sock可写性

}

这里,perror("connect")语句查询ERROR值为“Operation now

inprogress”,表明非阻塞connect立即返回的状态为正在建立三次握手;如果不想出现这种情况,可以将以上关于非阻塞设置socket的语
句放到connect之后,就会在阻塞方式下等待connect完成,但仍需要作进一步检查。对于非阻塞方式,下一步就可以通过select自定义超时时
间(通常比阻塞方式下connect超时时间短),并进一步检查是否连接错误和规定时间内套接口可读写性。

2.select超时设置问题

如果设置connect为非阻塞函数后,进行select时只关注writefds,忽略readfds,exceptfds,可能出现一个问题:本来不想由于connect阻塞等太久,结果用select后反而傻等了。

如果在connect后开始select,只关注writefds,设置的超时是10秒,在
connect发出[SYN]后:假定目标IP的主机不存在或者是目标端口给防火墙过滤了,那么你等再久也不会有任何回复,这时候如果是阻塞
connect可能要15秒才返回,那么你10秒就返回了,这种情况就赚了5秒。

然而假定connect的目标IP主机是存在的也没防火墙,只是端口是没打开的,在
connect发出[SYN]后的1秒系统已经收到目标主机回复的[RST,ACK],也就是说系统此时已经知道这个端口是连接不上的了,但是应用程序只
关注writefds,后面的9秒钟select就会傻傻的等待下去……原本以为用select来减少不必要的等待时间,如果不设置参数
exceptfds,这时候反而浪费时间。

3.send/recv 返回值


于send、recv函数用于已连接的数据报或流式套接口s进行数据的接收。所以在非阻塞socket的客户端程序中recv、send函数成功返回并不
代表对端一定收到了发送的消息。tcp协议本身是可靠的,并不等于应用程序用tcp发送数据就一定是可靠的。不管是否阻塞,send发送的大小,并不代表
对端recv到多少的数据.

关于recv返回值,百度的解释:

1).若无错误发生,recv()返回读入的字节数。

2).如果连接已中止,返回0。

3).否则的话,返回SOCKET_ERROR错误。

  如果套接口为SOCK_STREAM类型,并且远端“优雅”地中止了连接,那么recv()一个数据也不读取,立即返回。如果立即被强制中止,那么recv()将以WSAECONNRESET错误失败返回。

接收数据时perror时常遇到"Resource
temporarilyunavailable"的提示,errno代码为11(EAGAIN)。这表明你在非阻塞模式下调用了阻塞操作,在该操作没有完
成就返回这个错误,这个错误不会破坏socket的同步,不用管它,下次循环接着recv就可以。对非阻塞socket而言,EAGAIN不是一种错误。
在VxWorks和Windows上,EAGAIN的名字叫做EWOULDBLOCK。其实这算不上错误,只是一种异常而已。

  另外,如果出现EINTR即errno为4,错误描述Interrupted systemcall,即由于信号中断导致操作失败,也应该继续。

val= recv(client_sock,info,length,MSG_NOSIGNAL);
      if(val< 0)//判断是否网络无数据或接收缓冲区是否准备好
      { 
            if(val==EAGAIN||EWOULDBLOCK||EINTR)
            {
                 printf("Recvdata Timeout.Waiting...\n");
                 return1;
             }
             else//网络异常断开或阻塞;需重新连接
             {
                 perror("recv");
                 return-1;
             }
      }

elseif(val == 0)//server端正常关闭,需重新连接

{

TCP_CONNET_FLAG=0;//关闭TCP连接flag
         printf("socketclose nomally\n");
         return0;
       }

else//返回接收字节大于零,正常接收数据
       {

...//对接收缓冲区info的每个字节作读写操作处理

return 1;

}

关于send函数在阻塞模式和非阻塞模式下的区别:

在阻塞模式下,send函数的过程是将应用程序请求发送的数据拷贝到发
送缓存中发送并得到确认后再返回.但由于发送缓存的存在,表现为:如果发送缓存大小比请求发送的大小要大,那么send函数立即返回,同时向网络中发送数
据;否则,send向网络发送缓存中不能容纳的那部分数据,并等待对端确认后再返回(接收端只要将数据收到接收缓存中,就会确认,并不一定要等待应用程序
调用recv);
      在非阻塞模式下,send函数的过程仅仅是将数据拷贝到协议栈的缓存区而已,如果缓存区可用空间不够,则尽能力的拷贝,返回成功拷贝的大小;如缓存区可用空间为0,则返回-1,同时设置errno为EAGAIN.

当客户通过Socket提供的send函数发送大的数据包时,就可能返回一个EGGAIN的错
误。该错误产生的原因是由于send函数中的size变量大小超过了tcp_sendspace的值。tcp_sendspace定义了应用在调用
send之前能够在kernel中缓存的数据量。当应用程序在socket中设置了O_NDELAY或者O_NONBLOCK属性后,如果发送缓存被占
满,send就会返回EAGAIN的错误。

为了消除该错误,有三种方法可以选择:
  1).调大tcp_sendspace,使之大于send中的size参数
  ---no -p -otcp_sendspace=65536
  2).在调用send前,在setsockopt函数中为SNDBUF设置更大的值

intopt=SO_REUSEADDR;
     setsockopt(tcp_client_sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
          opt = 256*1024;//512k
          int optlen = sizeof(int);
          setsockopt(tcp_client_sock,SOL_SOCKET,SO_SNDBUF,&opt,sizeof(int));
          getsockopt(tcp_client_sock,SOL_SOCKET,SO_SNDBUF,&opt,&optlen); 
  3).使用write替代send,因为write没有设置O_NDELAY或者O_NONBLOCK

在CSDN中看到有这样一种情况:

假如发送端流量大于接收端的流量(意思是epoll所在的程序读比转发的socket要快),由于是非阻塞的socket,那么send()函数虽
然返回,但实际缓冲区的数据并未真正发给接收端,这样不断的读和发,当缓冲区满后会产生EAGAIN错误,同时,不理会这次请求发送的数据.

所以,需要根据send()函数返回值及errno值作进一步处理。遇到该情况,函数要求尽量将
数据写完再返回,或通过更改发送缓冲区大小。当写缓冲已满(send()返回-1,且errno为EAGAIN),那么会等待后再重试send().这种
方式并不很完美,在理论上可能会长时间的阻塞在socket的send()中,但暂没有更好的办法.

SendFlag&=TCP_CONNET_FLAG;//测试tcp_client_sock描述符可写且TCP连接存在
           if(SendFlag==1)//尝试发送
           {
              res=send_client_info(tcp_client_sock,buf,45);            
               if(res<0)
               {
                 if(errno == EINTR)//当socket是非阻塞时,如返回此错误,表示写缓冲队列已满,返回后判断网络状态再重试.
                   return -1;
                 if(errno == EAGAIN)//发送缓冲区剩余0字节,延时等待发送;
                 {
                      usleep(10000);
                      res=send_client_info(tcp_client_sock,buf,45);
                 }
               }
          }
          else
          {
          ...//发送缓冲区数据暂存,等待连接正常再发
          }

linux编程环境中,如果TCP连接断开后继续发数据的时候,不仅send()的返回值会有反
映,而且还会像系统发送一个异常消息,如果不作处理,系统会出BrokePipe,程序会退出。为此,send()函数的最后一个参数可以设
MSG_NOSIGNAL,禁止send()函数向系统发送异常消息。

3.close(socket)后send/recv数据的问题

如果在发送数据的过程中send()没有完成,还有数据没发送,而调用了closesocket(),以前一般采取的措施是shutdown(s,SD_BOTH),但是数据将会丢失。

  如果设计程序功能要求待未发送完的数据发送出去后再关闭socket,进行如下操作:

  struct linger {

  u_short l_onoff;

  u_short l_linger;

  };

  linger m_sLinger;

  m_sLinger.l_onoff = 1;//在调用closesocket()时还有数据未发送完,允许等待

  //若m_sLinger.l_onoff=0;则调用closesocket()后强制关闭

  m_sLinger.l_linger = 5;//设置等待时间为5秒

  setsockopt( s, SOL_SOCKET, SO_LINGER,( const char* )&m_sLinger, sizeof( linger ) );

若设置了SO_LINGER并确定了非零的超时间隔,则closesocket()调用阻塞进
程,直到所剩数据发送完毕或超时。这种关闭称为“优雅的”关闭。请注意如果套接口置为非阻塞且SO_LINGER设为非零超时,则
closesocket()调用将以WSAEWOULDBLOCK错误返回。 

WSAEWOULDBLOCK:该套接口设置为非阻塞方式且SO_LINGER设置为非零超时间隔。

[转] - linux下使用write\send发送数据报 EAGAIN : Resource temporarily unavailable 错的更多相关文章

  1. linux下启动dbca或netmgr类的图形界面报错解决

    linux下启动dbca或netmgr类的图形界面报错解决    Xlib: connection to ":0.0" refused by server Xlib: No pro ...

  2. linux 程序无缘无故推出 没有core文件 broken pipe Resource temporarily unavailable

    问题 1. linux socket 服务端程序 无缘无故退出 . 2. 客户端大量访问服务端后,出现  Resource temporarily unavailable错误 问题分析: 1. 是否有 ...

  3. Linux重启mysql Error getting authority: Error initializing authority: Could not connect: Resource temporarily unavailable (g-io-error-quark, 27)

    问题: Linux下重启mysql: systemctl restart mysqld 出现以下错误: Error getting authority: Error initializing auth ...

  4. TNS-12518,TNS-12536,TNS-00506,Linux Error: 11: Resource temporarily unavailable

    TNS-12518: TNS:listener could not hand off client connection TNS-12536: TNS:operation would block  T ...

  5. linux. -bash: fork: retry: Resource temporarily unavailable错误

    切换用户或登陆服务器后执行ls命令报错: -bash: fork: retry: Resource temporarily unavailable 上面这段错误提示的本质是Linux操作系统无法创建更 ...

  6. linux下, 再次遇到使用thinkphp的模板标签时,报错used undefined function \Think\Template\simplexml_load_string() 是因为没有安装 php-xml包

    linux下, 使用thinkphp的模板标签,如 eq, gt, volist defined, present , empty等 标签时, 报错: used undefined function ...

  7. linux下recv 、send阻塞、非阻塞区别和用法

    非阻塞IO 和阻塞IO: 在网络编程中对于一个网络句柄会遇到阻塞IO 和非阻塞IO 的概念, 这里对于这两种socket 先做一下说明:       基本概念: 阻塞IO:: socket 的阻塞模式 ...

  8. Python request 在linux上持续并发发送HTTP请求遇到 Failed to establish a new connection: [Errno 11] Resource temporarily unavailable

    并发数被限制 vim /etc/sysctl.conf 添加 net.ipv4.ip_local_port_range = 1024 65535   保存 /sbin/sysctl -p 让修改生效 ...

  9. linux下安装pkg-config时遇到"glib-2.0>=2.16"的错

    解决办法 如报错提示所述,加上:--with-internal-glib 即 ./configure --with-internal-glib 参考链接: http://stackoverflow.c ...

随机推荐

  1. 取出type="button" 和type="text" 里面的值显示在页面

    <script  type="text/JavaScript> function changeLink() { document.getElementById("nod ...

  2. POJ 2549 Sumsets hash值及下标

    题目大意:找到几何中的4个数字使他们能够组成 a+b+c=d , 得到最大的d值 我们很容易想到a+b = d-c 那么将所有a+b的值存入hash表中,然后查找能否在表中找到这样的d-c的值即可 因 ...

  3. Heap:Expedition(POJ 2431)

    远征队 题目大意:一部车要从一个地方走到另一个地方,开始的时候车的油箱有P升油,汽车每走1个距离消耗1升油,没有油汽车无法行驶,路上有加油站,可以为汽车加油,设汽车的油缸是无限大小的,问你汽车能否走到 ...

  4. BestCoder12 1002.Help him(hdu 5059) 解题报告

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5059 题目意思:就是输入一行不多于 100 的字符串(除了'\n' 和 '\r' 的任意字符),问是否 ...

  5. HDU 5754 Life Winner Bo (各种博弈) 2016杭电多校联合第三场

    题目:传送门 题意:一个国际象棋棋盘,有四种棋子,从(n,m)走到(1,1),走到(1,1)的人赢,先手赢输出B,后手赢输出G,平局输出D. 题解:先把从(n,m)走到(1,1)看做是从(1,1)走到 ...

  6. 1.单件模式(Singleton Pattern)

    意图:为了保证一个类仅有一个实例,并提供一个访问它的全局访问点. 1.简单实现(多线程有可能产生多个实例) public class CommonSigleton { /// <summary& ...

  7. Redis笔记(二)Redis的部署和启动

    Linux下Redis的部署和启动 下载安装介质 Redis官网地址:http://www.redis.io/目前最新版本是redis-3.0.3. 可以访问 http://download.redi ...

  8. UML中的关联关系

    UML中的关联关系其内在意思就是has a 如图:  相对于依赖关系,关联关系在代码中有所体现.上图中的关联关系在代码中体现为       其中water 中将Climate作为其中的属性. 当然,关 ...

  9. laravel数据库查询是use方法的使用

    ){                return $query->where('effectivetime','<',date('Y-m-d'));             }else{ ...

  10. Android广播机制概述

    1.Android广播机制概述 Android广播分为两个方面:广播发送者和广播接收者,通常情况下,BroadcastReceiver指的就是广播接收者(广播接收器).广播作为Android组件间的通 ...