epoll_wait()返回可用uid时,对uid取状态,本该是BROKEN的,却取到CLOSED,然而,不能像处理BROKEN事件那样处理CLOSED事件,这样移除不了CLOSED事件,于是epoll_wait不断返回该uid,就造成了死循环。跟踪代码至底层,寻找原因。
int CUDTUnited::epoll_remove_usock(const int eid, const UDTSOCKET u)
{
int ret = m_EPoll.remove_usock(eid, u);
CUDTSocket* s = locate(u);
if (NULL != s)
{
s->m_pUDT->removeEPoll(eid);
}
//else
//{
// throw CUDTException(5, 4);
//}
return ret;
}
CUDTSocket* CUDTUnited::locate(const UDTSOCKET u)
{
CGuard cg(m_ControlLock);
map<UDTSOCKET, CUDTSocket*>::iterator i = m_Sockets.find(u);
if ((i == m_Sockets.end()) || (i->second->m_Status == CLOSED))
return NULL;
return i->second;
}
void CUDT::removeEPoll(const int eid)
{
// clear IO events notifications;
// since this happens after the epoll ID has been removed, they cannot be set again
set<int> remove;
remove.insert(eid);
s_UDTUnited.m_EPoll.update_events(m_SocketID, remove, UDT_EPOLL_IN | UDT_EPOLL_OUT, false);
CGuard::enterCS(s_UDTUnited.m_EPoll.m_EPollLock);
m_sPollID.erase(eid);
CGuard::leaveCS(s_UDTUnited.m_EPoll.m_EPollLock);
}
CUDTUnited::epoll_remove_usock里,先locate目前uid的位置,但如果此时uid的状态是CLOSED,则返回NULL, 于是,epoll_remove_usock无法再继续调用removeEPoll,所以无法移除epoll事件。
但为什么会发生CLOSED事件呢?按照作者的原意,应该是只会发生BROKEN事件,不会发生CLOSED事件的,继续查找原因。
首先看看BROKEN事件怎么发生的。
客户端疑似断开十秒以上之后, CUDT::checkTimers()做以下操作
……
……
m_bClosing = true;
m_bBroken = true;
m_iBrokenCounter = 30;
// update snd U list to remove this socket
m_pSndQueue->m_pSndUList->update(this);
releaseSynch();
// app can call any UDT API to learn the connection_broken error
s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN | UDT_EPOLL_OUT | UDT_EPOLL_ERR, true);
CTimer::triggerEvent();
……
……
在这里把m_bBroken置为true,并触发epoll事件。
然而,在epoll_wait返回事件之前,还可能发生这个:
#ifndef WIN32
void* CUDTUnited::garbageCollect(void* p)
#else
DWORD WINAPI CUDTUnited::garbageCollect(LPVOID p)
#endif
{
CUDTUnited* self = (CUDTUnited*)p;
CGuard gcguard(self->m_GCStopLock);
while (!self->m_bClosing)
{
self->checkBrokenSockets();
……
……
void CUDTUnited::checkBrokenSockets()
{
CGuard cg(m_ControlLock);
// set of sockets To Be Closed and To Be Removed
vector<UDTSOCKET> tbc;
vector<UDTSOCKET> tbr;
for (map<UDTSOCKET, CUDTSocket*>::iterator i = m_Sockets.begin(); i != m_Sockets.end(); ++ i)
{
// check broken connection
if (i->second->m_pUDT->m_bBroken)
{
if (i->second->m_Status == LISTENING)
{
// for a listening socket, it should wait an extra 3 seconds in case a client is connecting
if (CTimer::getTime() - i->second->m_TimeStamp < 3000000)
continue;
}
else if ((i->second->m_pUDT->m_pRcvBuffer != NULL) && (i->second->m_pUDT->m_pRcvBuffer->getRcvDataSize() > 0) && (i->second->m_pUDT->m_iBrokenCounter -- > 0))
{
// if there is still data in the receiver buffer, wait longer
continue;
}
//close broken connections and start removal timer
i->second->m_Status = CLOSED;
i->second->m_TimeStamp = CTimer::getTime();
tbc.push_back(i->first);
m_ClosedSockets[i->first] = i->second;
……
……
GC线程是UDT的垃圾回收处理,在UDT调用cleanup(),之前,会一直处于checkBrokenSocket和阻塞的循环中。
然后在checkBrokenSocket里,当socket的m_bBroken为true时,m_Status的状态被置为CLOSED。
所以,这时候再用getsocketstate取socket的状态,就会取到CLOSED,也就是明明是BROKEN事件,硬生生变成了CLOSED事件!然后接下去epoll事件的移除就失败了。
于是,修改如下,
把
int CEPoll::remove_usock(const int eid, const UDTSOCKET& u)
{
CGuard pg(m_EPollLock);
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
if (p == m_mPolls.end())
throw CUDTException(5, 13);
p->second.m_sUDTSocksIn.erase(u);
p->second.m_sUDTSocksOut.erase(u);
p->second.m_sUDTSocksEx.erase(u);
return 0;
}
改为
int CEPoll::remove_usock2(const int eid, const UDTSOCKET& u)
{
CGuard pg(m_EPollLock);
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
if (p == m_mPolls.end())
throw CUDTException(5, 13);
p->second.m_sUDTSocksIn.erase(u);
p->second.m_sUDTSocksOut.erase(u);
p->second.m_sUDTSocksEx.erase(u);
p->second.m_sUDTWrites.erase(u);
p->second.m_sUDTReads.erase(u);
p->second.m_sUDTExcepts.erase(u);
return 0;
}
并去掉CUDTUnited::epoll_remove_usock()中对removeEPoll()的调用。
这是比较简单也比较粗糙的改法,应该有更方便的思路才对。
- UDT中的epoll
epoll 是为处理大量句柄而改进的poll,在UDT中也有支持.UDT使用了内核提供的epoll,主要是epoll_create,epoll_wait,epoll_ctl,UDT定义了CEPollD ...
- 快来体验快速通道,netty中epoll传输协议详解
目录 简介 epoll的详细使用 EpollEventLoopGroup EpollEventLoop EpollServerSocketChannel EpollSocketChannel 总结 简 ...
- android 自定义adapter和线程结合 + ListView中按钮滑动后状态丢失解决办法
adapter+线程 1.很多时候自定义adapter的数据都是来源于服务器的,所以在获取服务器的时候就需要异步获取,这里就需要开线程了(线程池)去获取服务器的数据了.但这样有的时候adapter的中 ...
- ubuntu中vi在编辑状态下方向键不能用的解决
ubuntu中vi在编辑状态下方向键不能用,还有回格键不能删除等,我们平时习惯的一些键都不能使用. 解决办法: 可以安装vim full版本,在full版本下键盘正常,安装好后同样使用vi命令. 安装 ...
- 如何设置Installshield中 feature的选中状态
原文:如何设置Installshield中 feature的选中状态 上一篇: 使用strtuts2的iterator标签循环输出二维数组之前一直有筒子问如何设置Installshield中 feat ...
- Hibernate中的对象有三种状态
Hibernate中的对象有三种状态: 瞬时状态 (Transient),持久状态 (Persistent), 1. 脱管状态 (Detached) 1. 1. 瞬时状态 (Transient) 由 ...
- Apache beam中的便携式有状态大数据处理
Apache beam中的便携式有状态大数据处理 目标: 什么是 apache beam? 状态 计时器 例子&小demo 一.什么是 apache beam? 上面两个图片一个是正面切图,一 ...
- Pvmove中断后恢复LV状态
Pvmove中断后恢复LV状态 pvmove执行时关闭中断窗口后,pvmove进程会被强制杀掉,从而导致lv的状态异常,无法重新进行pvmove和其他lvm镜像增加相关操作,可以通过如下方式修复: ...
- Unity 2D游戏开发教程之游戏中精灵的跳跃状态
Unity 2D游戏开发教程之游戏中精灵的跳跃状态 精灵的跳跃状态 为了让游戏中的精灵有更大的活动范围,上一节为游戏场景添加了多个地面,于是精灵可以从高的地面移动到低的地面处,如图2-14所示.但是却 ...
随机推荐
- git安装和初次使用
为了知道某人安装git,我也是重装啊. 1.下载git并安装 2.打开我的电脑,右键属性,选择高级设置,打开环境变量设置: 3.键盘输入窗口键+r,或者点击开始->运行 输入cmd 在新打开的命 ...
- 自定义iOS 中推送消息 提示框
看到标题你可能会觉得奇怪 推送消息提示框不是系统自己弹出来的吗? 为什么还要自己自定义呢? 因为项目需求是这样的:最近需要做 远程推送通知 和一个客服系统 包括店铺客服和官方客服两个模块 如果有新的消 ...
- [AngularJS] AngularJS系列(7) 进阶篇之promise
目录 使用promise 补充说明 $q.all $q.when 在上节中,我们在http中使用了then 和 在ngResource中返回了一个'延迟对象'. 本节介绍一下angular中的prom ...
- Vagrant入门
简单地说,Vagrant让我们可以通过代码的方式快速地.可重复地创建针对不同虚拟环境的虚拟机,包括Virtualbox.AWS.Docker等.它使得我们可以一次性地.自动创建多个环境相同的虚拟机,对 ...
- 自定义UIButton
偶尔逛简书能看见很多值得记下来的东西,有的接触过有的没接触过,接触过的也可能过段时间就忘记了,再上手的时候可能手足无措,所以决定有些觉得值得记下来的东西还是记录一下.博客是个好地方,因为很多人都能搜索 ...
- 部署.NET开发环境
昨晚把家里的电脑重新部署.NET开发环境.从晚上21点安装到今天凌晨3点多才完成,还算顺利,但是耗时最漫长莫过于在安装Visual Studio 2015 Update3...... 第一,全新安装W ...
- DataTable转实体Model,DataRow转实体Model,DataTable转泛型T,DataRow转泛型T
前言,此方法利用反射将DataRow转成实体,由于反射性能不行,大家就看看就行了吧. 代码来啦 using System; using System.Collections.Generic; usin ...
- ASP.NET MVC 了解FileResult的本质
FileResult是一个基于文件的ActionResult,利用FileResult我们可以很容易地将从某个物理文件的内容响应给客户端.ASP.NET MVC定义了三个具体的FileResult,分 ...
- 服务器Config文件不能查看的问题
由于某种需求,需要从IIS发布的服务中下载扩展名为config的文件,但是发布文件后,在浏览器无法查看文件.根据反馈的的错误提示,大致说config属于配置文件,处于安全考虑,不能随便浏览. 如果 ...
- Mybats中字符串判断
<if test=" name=='你好' "> <if> 这样会有问题,换成 <if test=' name=="你好" ' ...