如果服务端重启,那么客户端的长连接会怎么样

这里记录一次服务端重启时,使用winshark的抓包过程;

场景是:SDK 建立对 服务端的长连接,客户端连接策略是:

失活判断: 一条连接 180s都没有read到数据;
保活判断: 每秒检查一次,连续60次检查都为空闲,那么发送一次keeplive包。

重连逻辑: 如果连接断开,那么会以2s 、 4s、  6s、  8s...这样的递增产生的时延,去重连,每次连接等待5s判断连接超时而被认为连接失败。


客户端首先建立好对服务端的连接,然后关闭服务端,比如kill指令;


抓包分析:

1.由于是服务端被kill了,那么client立即感知到连接被关闭,recv=0;

2.此时服务端正在回收各种资源中,包括socket的资源,而客户端2s后执行断开重连的操作,居然连接成功了,并发送消息成功了。客户端这条连接被认为是成功。

3.问题来了,大约过了一会,服务端会发送一个rst指令给客户端,这个是通过抓包看得到的,然而使用ndk编码实现的sdk,工作在Android模拟器里,居然没有触发select的 

  读事件;因为没有被立即触发,那么客户端就要等到一个超时周期,直到判断连接失活了,才closesocket。而在此期间,这条tcp连接都被认为是成功的。

  问题是:为什么既然对端发了一个rst指令,而此端的select怎么没有检查到readable事件呢;代码如下:

void CTCPSocket::OnSelectEvent()
{
	struct timeval tv ;
	tv.tv_sec = 0;
	tv.tv_usec = 1000*100 ;

	fd_set readset ;
	fd_set sendset ;
	fd_set exceptionalset;

	FD_ZERO( &readset ) ;
	FD_ZERO( &exceptionalset );
	FD_ZERO( &sendset ) ;

	FD_SET( m_hSocket, &readset ) ;
	FD_SET( m_hSocket, &exceptionalset ) ;

	fd_set * pSendSet = NULL ;
	if (  CheckWriteEvent() )
	{
		FD_SET( m_hSocket, &sendset ) ;
		pSendSet = &sendset ;
	}

	DEBUG_LOG("SELECT");

	int rc = select( m_hSocket+1, &readset, pSendSet , &exceptionalset, &tv ) ;
	if (rc < 0)
	{
		m_pNetHandler->OnError( "CTCPSocket::Execute select err...%d", errno ) ;
		ErrEvent() ;
		return;
	}

	if (1==m_InConnected)
	{	
		DEBUG_LOG("OnSelectEvent checkConnectEvent");
		ConnectEvent( &readset, &sendset ) ;	
		return;
	}

	int nErr = 0 ;

	if (  FD_ISSET(m_hSocket,&exceptionalset) )
	{
		DEBUG_LOG("exceptionalEvent()");
	}

	if ( FD_ISSET( m_hSocket, &readset ) )
	{
		DEBUG_LOG("ReadEvent()");
		nErr = ReadEvent() ;
	}

	if (/* nErr == 0 && pSendSet != NULL && */FD_ISSET( m_hSocket, &sendset) )
	{
		DEBUG_LOG("SendEvent()");
		nErr = SendEvent() ;
	}

	if ( nErr != 0 )
	{
		ErrEvent() ;
	}

}


难道是我的这段基于select io 模式的代码有问题?

然后我首先使用python写了一段select的代码,做相同的测试,发现很快就触发了readevent;

然后我再把这段代码在linux运行,也很快被对端的rst指令触发了readevent事件;


相同代码,在不同的平台的执行效果,可能就是不如预期的。除非把这一段代码,在不同的平台都做了完整的测试。这段代码被使用在客户端里,由此可以体会到客户端开发的一个难点,就是跨平台性。

我想不同平台对select的实现,是不是并不是完全一致的。比如我使用的Android模拟器就是一个linux内核的裁剪版本。这只能算一个猜测,但是这足以说明写一个平台的代码,是不能完全用在另一个平台的经验做主观上完全没问题的断定的。实际,相同代码的执行在不同的平台上执行结果,依旧充满不确定性吧。


那么针对我这个问题,保险起见,可以每次select之后,都可以直接触发一次readevent;以保证客户端代码的可用性吧。

(PS: 这样也解决不了问题:涉及测试时 read操作总是忙;最后我是怎么解决的呢,把断开连接重连的时延设置得更大了一旦,比如我我这次设置为6s,就避免了在服务端没有完全释放前,我又连接了上去;

但其实这个问题,我还是没法解释原因。

)



  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值