接口调用简单、多平台支持的RTSP-Server组件EasyRTSPServer客户端异常掉线导致部分通道阻塞问题解决方案
RTSP协议是一个流媒体协议,可以实现直播和点播形式的音频与视频流的播放。RTSP协议定义了多种服务器-客户端之间交互的接口,主要有OPTIONS,DESCRIBE,SETUP,PLAY,TEARDOWN,RECORD,ANNOUNCE。网络上已经有很多针对RTSP协议的文章,我这里不准备进行过多介绍。RTSP并不包括具体数据的传输,该功能一般由RTP与RTCP协议来实现,并可以通过TCP或UDP两种底层传输方式进行。
EasyRTSPServer是一套稳定、高效、可靠、多平台支持的RTSP-Server组件, 接口调用非常简单成熟,今天为大家分享关于EasyRTSPServer客户端异常掉线导致部分通道阻塞的问题
提出问题:
EasyRTSPServer在Linux环境下会因为客户端异常掉线后,偶现导致部分通道不能再被访问。
分析问题:
本来是分析服务端CLOSE_WAIT的问题,在调试的过程中, 却又发现了阻塞,认真分析了live555的事件调用流程后,发现只要从客户端收到数据,就会执行GenericMediaServer::ClientSession::noteLiveness(),在该函数中会删除上一次的超时事件,然后新建一个超时事件放入调度队列中,而客户端突然掉线,正常情况是会调用该超时任务, 在该任务中删除ClientSession, 在大量的客户端连接的情况下(128路)突然掉线, 就有几率会出现阻塞,从而导致没有调用到该超时事件,进而导致socket不能释放了。
销毁上一次的超时任务,创建一个新的超时任务
void GenericMediaServer::ClientSession::noteLiveness() {
if (fOurServerMediaSession != NULL) fOurServerMediaSession->noteLiveness();
if (fOurServer.fReclamationSeconds > 0) {
pEnvironment->taskScheduler().rescheduleDelayedTask(fLivenessCheckTask,
fOurServer.fReclamationSeconds*1000000,
(TaskFunc*)livenessTimeoutTask, this);
}
}
超时任务实现如下
void GenericMediaServer::ClientSession::livenessTimeoutTask(ClientSession* clientSession) {
delete clientSession;
}
加了N多的打印日志, 最终分析为epoll检测到了socket有事件,并且调用了相应的处理函数, 在该函数中,调用了recvfrom而导致阻塞… 恍然大悟, 赶紧搜一下SO_RCVTIMEO,果然,在live555所有代码中,没有搜到该关键字。
解决问题:
在创建socket后,设置接收数据超时时间即可。
#if defined(__WIN32__) || defined(_WIN32)
DWORD msto = (DWORD)500;
setsockopt(newSocket, SOL_SOCKET, SO_SNDTIMEO, (char *)&msto, sizeof(msto) );
setsockopt(newSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&msto, sizeof(msto) );
#else
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 500*1000;
setsockopt(newSocket, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof tv);
setsockopt(newSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof tv);
#endif