接手他人代码,单进程单线程异步复用,用的裸接口,发现线上有个bug,之前用的边缘触发,改成水平触发后,问题不断;
今天发现,有时write evt事件在某些条件下,一直得不到调用,反而一直在调用 read evt 的callback,导致死循环;
lsof 发现,这个进程对应的某些fd已经完全被系统回收了(即已经显示不了tcp两对ip+port了)
问题清晰的表明:write evt 是生效的,但为什么一直调用read的callback呢?read evt 实际都已经关掉了;
于是查代码,最后,发现上层epoll_wait 之后的event处理流程问题:
-
-
bool hasErr = false;
-
if ((events[i].events & EPOLLIN)
-
|| (events[i].events & EPOLLHUP)
-
|| (events[i].events & EPOLLERR))
-
{
-
if (sess->onRead(eed->side) != 0)
-
{
-
hasErr = true;...
-
}
-
}
-
//处理可写事件
-
if ((events[i].events & EPOLLOUT) && (!hasErr))
-
{
-
if (sess->onWrite(eed->side) != 0)
-
{
-
hasErr = true;...
-
}
-
}
看到这里,一下就明白了:某些条件下,fd被另外一端直接关闭了,此时虽然注册的是 EPOLLOUT,但返回的event实际为 EPOLLHUP,
很简单的就修正了,之前花了很多时间在查callback函数本身的实现和逻辑;结果是上层事件处理问题;
呃,一堆相似的问题,加上之前的循环buf的bug,还有read,write返回和errorno等等问题,处理了一次又一次;重复的临时的造轮子,
确实太容易出问题了;把这些都封装好,就不要去碰了,要不肯定还会一次又一次写出相似的代码,但总会这里有问题那里有bug;
蛋疼的很,这些问题其实碰到过n次了
btw,最后在网上顺便查了下 EPOLLHUP 事件:
当socket的一端认为对方发来了一个不存在的4元组请求的时候,会回复一个RST响应,在epoll上会响应为EPOLLHUP事件,目前我已知的两种情况会发响应RST
[1] 当客户端向一个没有在listen的服务器端口发送的connect的时候 服务器会返回一个RST 因为服务器根本不知道这个4元组的存在
[2] 当已经建立好连接的一对客户端和服务器,客户端突然操作系统崩溃,或者拔掉电源导致操作系统重新启动(kill pid或者正常关机不行的,因为操作系统会发送FIN给对方).
这时服务器在原有的4元组上发送数据,会收到客户端返回的RST,因为客户端根本不知道之前这个4元组的存在
阅读(6733) | 评论(0) | 转发(0) |