linux下使用write\send发送数据报 EAGAIN : Resource temporarily unavailable 错
http://badqiu.iteye.com/blog/1149965
linux下使用write\send发送数据报 EAGAIN : Resource temporarily unavailable 错
首先是我把套接字设置为异步的了,然后在使用write发送数据时采取的方式是循环发送大量的数据;由于是异步的,write\send将要发送的数据提交到发送缓冲区后是立即返回的,并不需要对端确认数据已接收。在这种情况下是很有可能出现发送缓冲区被填满,导致write\send无法再向缓冲区提交要发送的数据。因此就产生了Resource temporarily unavailable的错误,EAGAIN 的意思也很明显,就是要你再次尝试。
把发送部分修改如下
- int SeanSend(int fd, void *buffer, int length)
- {
- int bytes_left;
- int written_bytes;
- char *ptr;
- ptr=(char *)buffer;
- bytes_left=length;
- while(bytes_left>0)
- {
- /* 开始写*/
- written_bytes=write(fd, ptr, bytes_left);
- if(written_bytes<=0) /* 出错了*/
- {
- if(errno==EINTR) /* 中断错误 我们继续写*/
- {
- continue;
- printf("[SeanSend]error errno==EINTR continue\n");
- }
- else if(errno==EAGAIN) /* EAGAIN : Resource temporarily unavailable*/
- {
- sleep(1);//等待一秒,希望发送缓冲区能得到释放
- continue;
- printf("[SeanSend]error errno==EAGAIN continue\n");
- }
- else /* 其他错误 没有办法,只好退了*/
- {
- printf("[SeanSend]ERROR: errno = %d, strerror = %s \n"
- , errno, strerror(errno));
- return(-1);
- }
- }
- bytes_left-=written_bytes;
- ptr+=written_bytes;/* 从剩下的地方继续写?? */
- }
- return length;
- }
非阻塞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连接
...
}
但是在网上查询发现:当我们以非阻塞的方式来进行连接的时候,返回的结果如果是-1,这并不代表这次连接发生了错误,如果它的返回结果是EINPROGRESS,那么就代表连接还在进行中。后面可以通过poll或者select来判断socket是否可写,如果可以写,说明连接完成了。
更改如下:
//先定位为非阻塞模式,立即返回状态;如有错误存为SO_ERROR值
if((flags =fcntl(tcp_client_sock,F_GETFL, 0 )) < 0)
else
这里,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返回值,百度的解释:
另外,如果出现EINTR即errno为4,错误描述Interrupted systemcall,即由于信号中断导致操作失败,也应该继续。
关于send函数在阻塞模式和非阻塞模式下的区别:
当客户通过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设置更大的值
3).使用write替代send,因为write没有设置O_NDELAY或者O_NONBLOCK
假如发送端流量大于接收端的流量(意思是epoll所在的程序读比转发的socket要快),由于是非阻塞的socket,那么send()函数虽然返回,但实际缓冲区的数据并未真正发给接收端,这样不断的读和发,当缓冲区满后会产生EAGAIN错误,同时,不理会这次请求发送的数据.
所以,需要根据send()函数返回值及errno值作进一步处理。遇到该情况,函数要求尽量将数据写完再返回,或通过更改发送缓冲区大小。当写缓冲已满(send()返回-1,且errno为EAGAIN),那么会等待后再重试send().这种方式并不很完美,在理论上可能会长时间的阻塞在socket的send()中,但暂没有更好的办法.
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设置为非零超时间隔。
更多推荐
所有评论(0)