recv-q堆积原因和解决办法
connectionresetbypeer表示本端tcp连接收到了对端发送的RST段。
brokenpipe是一个信号,表示对已关闭的管道进行读写操作。
rstflag段产生的原因有几下几种:
1.请求的目标端口未开放,会收到rst段。
2.socketRecv-Q中的数据未完全被应用程序读取,而关闭该socket,会发送rst段。
3.向已关闭的socket发送数据,会发送rst段。
recv是阻塞还是非阻塞的
socket分为阻塞和非阻塞两种,可以通过setsockopt,或者更简单的setblocking,settimeout设置。
阻塞式的socket的recv服从这样的规则:当缓冲区内有数据时,立即返回所有的数据;当缓冲区内无数据时,阻塞直到缓冲区中有数据。
非阻塞式的socket的recv服从的规则则是:当缓冲区内有数据时,立即返回所有的数据;当缓冲区内无数据时,产生EAGAIN的错误并返回(在Python中会抛出一个异常)。
两种情况都不会返回空字符串,返回空数据的结果是对方关闭了连接之后才会出现的。由于TCP的socket是一个流,因此是不存在“读完了对方发送来的数据”这件事的。
你必须要每次读到数据之后,根据数据本身来判断当前需要等待的数据是否已经全部收到,来判断是否进行下一个recv。
可以看一下hiredis库的接口设计,hiredis中的Reader有两个接口,分别是feed和gets,feed每次送入一部分数据,不需要保证是正确分片的;gets则返回已经得到的完整的结果,如果返回False,表示已经没有新的结果。基本上所有的TCP的socket编程都是遵循这样的方法:读入新数据;判断有没有完整的新消息;处理新消息,或者等待更多数据。
setsockopt()的用法
⒈设置调用closesocket()后,仍可继续重用该socket。调用closesocket()一般不会立即关闭socket,而经历TIME_WAIT的过程。BOOLbReuseaddr=TRUE;setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(constchar*)&bReuseaddr,sizeof(BOOL));⒉如果要已经处于连接状态的soket在调用closesocket()后强制关闭,不经历TIME_WAIT的过程:BOOLbDontLinger=FALSE;setsockopt(s,SOL_SOCKET,SO_DONTLINGER,(constchar*)&bDontLinger,sizeof(BOOL));⒊在send(),recv()过程中有时由于网络状况等原因,收发不能预期进行,可以设置收发时限:intnNetTimeout=1000;//1秒//发送时限setsockopt(socket,SOL_SOCKET,SO_SNDTIMEO,(char*)&nNetTimeout,sizeof(int));//接收时限setsockopt(socket,SOL_SOCKET,SO_RCVTIMEO,(char*)&nNetTimeout,sizeof(int));⒋在send()的时候,返回的是实际发送出去的字节(同步)或发送到socket缓冲区的字节(异步);系统默认的状态发送和接收一次为8688字节(约为8.5K);在实际的过程中如果发送或是接收的数据量比较大,可以设置socket缓冲区,避免send(),recv()不断的循环收发://接收缓冲区intnRecvBuf=32*1024;//设置为32Ksetsockopt(s,SOL_SOCKET,SO_RCVBUF,(constchar*)&nRecvBuf,sizeof(int));//发送缓冲区intnSendBuf=32*1024;//设置为32Ksetsockopt(s,SOL_SOCKET,SO_SNDBUF,(constchar*)&nSendBuf,sizeof(int));⒌在发送数据的时,不执行由系统缓冲区到socket缓冲区的拷贝,以提高程序的性能:intnZero=0;setsockopt(socket,SOL_SOCKET,SO_SNDBUF,(char*)&nZero,sizeof(nZero));⒍在接收数据时,不执行将socket缓冲区的内容拷贝到系统缓冲区:intnZero=0;setsockopt(s,SOL_SOCKET,SO_RCVBUF,(char*)&nZero,sizeof(int));⒎一般在发送UDP数据报的时候,希望该socket发送的数据具有广播特性:BOOLbBroadcast=TRUE;setsockopt(s,SOL_SOCKET,SO_BROADCAST,(constchar*)&bBroadcast,sizeof(BOOL));⒏在client连接服务器过程中,如果处于非阻塞模式下的socket在connect()的过程中可以设置connect()延时,直到accpet()被调用(此设置只有在非阻塞的过程中有显著的作用,在阻塞的函数调用中作用不大)BOOLbConditionalAccept=TRUE;setsockopt(s,SOL_SOCKET,SO_CONDITIONAL_ACCEPT,(constchar*)&bConditionalAccept,sizeof(BOOL));⒐如果在发送数据的过程中send()没有完成,还有数据没发送,而调用了closesocket(),以前一般采取的措施是shutdown(s,SD_BOTH),但是数据将会丢失。某些具体程序要求待未发送完的数据发送出去后再关闭socket,可通过设置让程序满足要求:structlinger{u_shortl_onoff;u_shortl_linger;};lingerm_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,(constchar*)&m_sLinger,sizeof(linger));参见:bind(),getsockopt(),ioctlsocket(),socket(),WSAAsyncSelect().