epoll问题求助!为什么在大压力下,epoll检测不到客户端连结断开呢?

sunnyboycao 2007-07-13 05:19:07
我的epoll不是所有的socket连结断开都能检测的到?是不是客户端突然断开,服务器永远都检测不到EPOLLIN事件?

我的服务器程序要在客户端断开之后主动清理客户端的一些信息,并退出登录!
是部分代码:
int comm_check_incoming(int nfds)
{
int i,fd;
PF *hdl = NULL;
struct epoll_event events[IM_MaxFD];

nfds=epoll_wait(epfd,events,IM_MaxFD,MAX_WAIT_TIME);

//处理所发生的所有事件
for(i=0;i<nfds;++i)
{
//printf("Read event = %d\n",events[i].events);
if(events[i].events& EPOLLIN )
{
debug(1,1)("XXXX:fd=%d,events=%x,EPOLLIN=%x\n",events[i].data.fd,events[i].events,EPOLLIN);

if ( (fd = events[i].data.fd) < 0) continue;
if ((hdl = g_stFdTable[fd].read_handler))
{
//g_stFdTable[fd].read_handler = NULL;
hdl(fd);
}
}
if (events[i].events & (EPOLLOUT))
{
if ((hdl = g_stFdTable[fd].write_handler))
{
//g_stFdTable[fd].write_handler = NULL;
hdl(fd);
}
}
}
return 1;
}


不是所有的连结断开都能打印这段调试信息
debug(1,1)("XXXX:fd=%d,events=%x,EPOLLIN=%x\n",events[i].data.fd,events[i].events,EPOLLIN);
...全文
1377 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
Novey 2008-07-21
  • 打赏
  • 举报
回复
可以参考下面代码:
957 res = read(c->sfd, c->rbuf + c->rbytes, c->rsize - c->rbytes);
958 if (res > 0) {
959 stats.bytes_read += res;
960 gotdata = 1;
961 c->rbytes += res;
962 continue;
963 }
964 if (res == 0) {
965 /* connection closed */
966 c->state = conn_closing;
967 return 1;
968 }

969 if (res == -1) {
970 if (errno == EAGAIN || errno == EWOULDBLOCK) break;
971 else return 0;
972 }
Novey 2008-07-21
  • 打赏
  • 举报
回复
上面这种memcached-1.1.13版本中的模型在client结束的时候会立刻触发epoll进行调度,一般情况下是触发read或者write,
你直接判断read/write的返回值就知道客户端是否是断开了。
Novey 2008-07-21
  • 打赏
  • 举报
回复
看到好多用epoll的人都使用上面那种模型,但我看到memcached中使用的epoll模型虽然类似,但更高效一些。

我所看的memcached-1.1.13版本原始代码,整个系统都是单进程单线程的,重点看epoll的几个函数调用就OK了。

/* create the listening socket and bind it */
l_socket = server_socket(settings.port); /* 其中server_socket创建一个监听的fd */
if (l_socket == -1) {
fprintf(stderr, "failed to listen\n");
exit(1);
}

....

event_init(); /* 初始化epoll */

....

/* create the initial listening connection */
if (!(l_conn = conn_new(l_socket, conn_listening, EV_READ | EV_PERSIST))) { /* 在conn_new中设定了event并调用epoll的event_add加入epoll */
fprintf(stderr, "failed to create listening connection");
exit(1);
}

....

/* enter the loop */
event_loop(0);


其中在event_handler中进入drive_machine处理客户端连接以及客户端/服务器端的io,并处理连接的断开/读写的阻塞等问题。


/* do as much I/O as possible until we block */
drive_machine(c);

while (!exit) {
/* printf("state %d\n", c->state);*/
switch(c->state) {
case conn_listening:
addrlen = sizeof(addr);
if ((sfd = accept(c->sfd, &addr, &addrlen)) == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
exit = 1;
break;
} else {
perror("accept()");
}
break;
}
if ((flags = fcntl(sfd, F_GETFL, 0)) < 0 ||
fcntl(sfd, F_SETFL, flags | O_NONBLOCK) < 0) {
perror("setting O_NONBLOCK");
close(sfd);
break;
}
newc = conn_new(sfd, conn_read, EV_READ | EV_PERSIST);
if (!newc) {
if (settings.verbose > 0)
fprintf(stderr, "couldn't create new connection\n");
close(sfd);
break;
}

break;
...

最好是去找原版代码看看,写memcached的人可是高手哦?

如果想要增加处理能力,引入多线程epoll来提高网络io,则可以看memcached-2.x.x版本,里面有很好的多eventbase的多线程epoll模型。

这里要注意,单进程有个fd限制,所以要注意以下代码(都是memcached-1.1.13原始代码):

1598 if (maxcore) {
1599 struct rlimit rlim_new;
1600 /*
1601 * First try raising to infinity; if that fails, try bringing
1602 * the soft limit to the hard.
1603 */
1604 if (getrlimit(RLIMIT_CORE, &rlim)==0) {
1605 rlim_new.rlim_cur = rlim_new.rlim_max = RLIM_INFINITY;
1606 if (setrlimit(RLIMIT_CORE, &rlim_new)!=0) {
1607 /* failed. try raising just to the old max */
1608 rlim_new.rlim_cur = rlim_new.rlim_max =
1609 rlim.rlim_max;
1610 (void) setrlimit(RLIMIT_CORE, &rlim_new);
1611 }
1612 }
1613 /*
1614 * getrlimit again to see what we ended up with. Only fail if
1615 * the soft limit ends up 0, because then no core files will be
1616 * created at all.
1617 */
1618
1619 if ((getrlimit(RLIMIT_CORE, &rlim)!=0) || rlim.rlim_cur==0) {
1620 fprintf(stderr, "failed to ensure corefile creation\n");
1621 exit(1);
1622 }
1623 }
1624
1625 /*
1626 * If needed, increase rlimits to allow as many connections
1627 * as needed.
1628 */
1629
1630 if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) {
1631 fprintf(stderr, "failed to getrlimit number of files\n");
1632 exit(1);
1633 } else {
1634 int maxfiles = settings.maxconns;
1635 if (rlim.rlim_cur < maxfiles)
1636 rlim.rlim_cur = maxfiles + 3;
1637 if (rlim.rlim_max < rlim.rlim_cur)
1638 rlim.rlim_max = rlim.rlim_cur;
1639 if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) {
1640 fprintf(stderr, "failed to set rlimit for open files. Try running as root or requesting smaller maxconns value.
\n");
1641 exit(1);
1642 }
1643 }

可以通过修改rlimit值来增大所能打开的fd数量哦?
由于memcached自身的业务处理就是内存管理和网络io,内存管理作者使用了slab内存管理,
充分发挥了epoll的高效,所以memcached速度非常快,是个很好的server端范例。

上面写的希望对大家写server端程序有所帮助。
redex 2007-07-17
  • 打赏
  • 举报
回复
把调试语句在读处理函数中打印.
read返回0或者返回-1且errno不等于EINTR/EAGAIN者为断开.
mymtom 2007-07-14
  • 打赏
  • 举报
回复
继续关注,学习。
sunnyboycao 2007-07-13
  • 打赏
  • 举报
回复
带上EPOLLHUP|EPOLLERR我也试过了,没有用!有的资料干脆说EPOLLHUP这个没什么用,要检查EPOLLIN事件的read是否返回小于等于0来判断断开与否!所以,我才把最初的EPOLLIN|EPOLLHUP|EPOLLERR改成现在这样子。

lu_zi(lu_zi)
难道只有超时才能解决这个东西吗?那样代价大,要轮训socket表!服务器性能会下降吧。
i_noname 2007-07-13
  • 打赏
  • 举报
回复
EPOLLHUP很重要,一定要检测的
lu_zi 2007-07-13
  • 打赏
  • 举报
回复
这个问题不是epoll的问题,而是tcp的超时机制造成的,不要相信它
用你自己的超时来处理这个问题吧
mymtom 2007-07-13
  • 打赏
  • 举报
回复
是不是需要处理其它的事件,如:EPOLLHUP,EPOLLERR

23,114

社区成员

发帖
与我相关
我的任务
社区描述
Linux/Unix社区 应用程序开发区
社区管理员
  • 应用程序开发区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧