概述
这里记录一次服务端重启时,使用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,就避免了在服务端没有完全释放前,我又连接了上去;
但其实这个问题,我还是没法解释原因。
)
最后
以上就是温暖绿茶为你收集整理的如果服务端重启,那么客户端的长连接会怎么样的全部内容,希望文章能够帮你解决如果服务端重启,那么客户端的长连接会怎么样所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复