Linux下服务器端使用EPOLL ACCEPT产生的问题
来源:互联网 发布:微信公众号树洞源码 编辑:程序博客网 时间:2024/06/01 09:48
最近查了3天一个技术BUT,开始并不知道是网络问题,一步步定位,最终确定是网络接收问题。
进而深入前人的代码才有所察觉,因这个问题是随机性,很难重现。找到了网络问题,立马写代码重现。果然立马重现了。确定是EPOLL accept问题。
因为我们的问题是服务器端 接收缓冲区中一直有数据,拿不走,连接是已建立。
看看我们的代码
问题出在EPOLL的模式上,EPOLL有水平模式 和 边缘模式,而这里监听句柄 和 客户端句柄都采用边缘
边缘的意思 是只有状态变化了才通知,即从可读->不可读->可读 才会返回 ,一直可读则只返回一次,
即下面的代码如果客户端同时大量连接,服务器端可能同时又多个连接事件,但accept 只会通知一次,导致后续的连接还在内核,所以服务器端应用没有去接收,
解决该问题,是把监听句柄 改为水平模式,其实默认就是水平。去掉EPOLLET即可;下次accept时可以吧剩余的连接取出来。
另一种方法是 在处理客户端连接 吧accept 改为 while 就像读写数据一样,消灭这次所有的事件才能继续;
即
while( ( remoteFd = accept(m_socket, ( struct sockaddr* ) &client, &length))>0)
1.建立监听socket
m_epoll = epoll_create( 128);
ev.data.fd = m_socket;
ev.events = EPOLLIN|EPOLLET;//边缘模式
epoll_ctl(m_epoll, EPOLL_CTL_ADD, m_socket, &ev);
2.处理客户端连接
nfds = epoll_wait(m_epoll, events, 32, m_timeout);
for (i = 0; i < nfds; i++)
{
if (events[i].data.fd < 0)
continue;
if (events[i].data.fd == m_socket)
{
struct sockaddr_in client;
socklen_t length = sizeof (client );
remoteFd = accept(m_socket, ( struct sockaddr* ) &client, &length);
if (remoteFd > 0)
{
printf("[CSocketServer::WaitForEvent] client ip=%s port=%d",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
fcntl(remoteFd, F_SETFL, fcntl(remoteFd, F_GETFL) | O_NONBLOCK);
ev.data.fd = remoteFd;
ev.events = EPOLLIN|EPOLLET|EPOLLHUP;//边缘模式
epoll_ctl(m_epoll, EPOLL_CTL_ADD, remoteFd, &ev);
}
continue;
}
else if (events[i].events & EPOLLIN)
{
}
else if (events[i].events & EPOLLHUP)
{
}
}
其实man epoll 中的例子监听句柄默认也是水平模式,客户端句柄才是边缘模式,估计前人是从网上拷贝下来,导致问题潜伏了N年
Example for Suggested Usage
While the usage of epoll when employed as a level-triggered interface does have the same semantics as poll(2), the edge-triggered usage requires more clari‐
fication to avoid stalls in the application event loop. In this example, listener is a non-blocking socket on which listen(2) has been called. The func‐
tion do_use_fd() uses the new ready file descriptor until EAGAIN is returned by either read(2) or write(2). An event-driven state machine application
should, after having received EAGAIN, record its current state so that at the next call to do_use_fd() it will continue to read(2) or write(2) from where it
stopped before.
#define MAX_EVENTS 10
struct epoll_event ev, events[MAX_EVENTS];
int listen_sock, conn_sock, nfds, epollfd;
/* Set up listening socket, 'listen_sock' (socket(),
bind(), listen()) */
epollfd = epoll_create(10);
if (epollfd == -1) {
perror("epoll_create");
exit(EXIT_FAILURE);
}
ev.events = EPOLLIN;//水平模式
ev.data.fd = listen_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
perror("epoll_ctl: listen_sock");
exit(EXIT_FAILURE);
}
for (;;) {
nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
if (nfds == -1) {
perror("epoll_pwait");
exit(EXIT_FAILURE);
}
for (n = 0; n < nfds; ++n) {
if (events[n].data.fd == listen_sock) {
conn_sock = accept(listen_sock, (struct sockaddr *) &local, &addrlen);
if (conn_sock == -1) {
perror("accept");
exit(EXIT_FAILURE);
}
setnonblocking(conn_sock);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = conn_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
&ev) == -1) {
perror("epoll_ctl: conn_sock");
exit(EXIT_FAILURE);
}
} else {
do_use_fd(events[n].data.fd);
}
}
}
- Linux下服务器端使用EPOLL ACCEPT产生的问题
- linux下epoll模型accept并发问题
- linux下epoll模型accept并发问题
- linux epoll accept 问题
- EPOLL下的accept(转载)
- EPOLL下的accept(转载)
- 在linux下使用socket通信,accept调用产生accept error:Invalid argument
- linux accept与epoll惊群问题
- epoll中accept的使用细节
- Linux下epoll的使用
- 关于在linux下epoll的使用问题
- epoll 的accept , read, write
- linux下的epoll
- Linux下的epoll
- Linux epoll的使用
- linux epoll的使用
- linux epoll 练习(服务器端)
- Linux 下使用epoll实现转发服务器的小DEMO
- MAC系统下apktool和dex2jar和jd-Gui的安装
- 安卓特色服务之定位服务(百度地图二)
- 常用的排序方法(代码总结)
- 一些常用的Android命令
- 论文写作技巧
- Linux下服务器端使用EPOLL ACCEPT产生的问题
- intellij idea中刷新gradle无效
- Tiny210v2( S5PV210 ) 平台下 FIMD 对应 的 framebuffer 驱动中,关于 video buffer 的理解
- Material Design时代
- 上帝造题五分钟
- 使用缓存的9大误区
- Long.parse和Number
- JQuery:视频+实战总结
- win7 win10下80端口被System进程占用的解决方法