epoll有两种触发模式,水平触发和边界触发。在监听listen socket的时候网上很多的代码里面选择边界触发,而且不是用while去accept,我们做如下的分析:建立listen socket之后,我们开始用epoll进行监听,如果某个时候很多请求到达,边界条件下触发了,但是我们调用accept处理一个连接,这个时候listen socket还有可读的,但是边界触发下,不会再触发了。也就是可能造成链接的丢失。
下面是一个简单的代码做一个测试:
服务器端的代码:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <sys/types.h>
#include <netinet/in.h>
#define MAXSOCKET 1024
#define BUFFSIZE 2048
void setnonblocking(int fd)
{
int f;
f=fcntl(fd,F_GETFL,0);
if(f<0)
{
perror("fcntl error!f_getfl\n");
exit(-1);
}
if(fcntl(fd,F_SETFL,f|O_NONBLOCK)<0)
{
perror("fcntl error!f_setfl\n");
exit(-1);
}
}
int main()
{
int sockfd,connectfd,confds[MAXSOCKET];
sockfd=socket(PF_INET,SOCK_STREAM,0);
if(sockfd<0)
{
perror(strerror(errno));
return -1;
}
unsigned value;
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&value,sizeof(value));
memset(confds,0,MAXSOCKET);
setnonblocking(sockfd);
struct sockaddr_in server;
socklen_t socklen=sizeof(struct sockaddr_in);
memset(&server,0,socklen);
server.sin_addr.s_addr=htonl(INADDR_ANY);
server.sin_port=htons(8989);
server.sin_family=AF_INET;
if(bind(sockfd,(struct sockaddr *)&server,socklen)<0)
{
perror(strerror(errno));
return -1;
}
int epollfd=epoll_create(MAXSOCKET);
if(epollfd<0)
{
perror(strerror(errno));
return -1;
}
struct epoll_event ev,events[MAXSOCKET];
memset(&ev,0,sizeof(ev));
ev.events=EPOLLIN|EPOLLET;
ev.data.fd=sockfd;
epoll_ctl(epollfd,EPOLL_CTL_ADD,sockfd,&ev);
int count=0;
int max=0;
listen(sockfd,MAXSOCKET);
char buf[BUFFSIZE];
for(;;)
{
max=epoll_wait(epollfd,events,MAXSOCKET,0);
if(max<0)
{
perror("epoll_wait error!\n");
continue;
}
int i;
for(i=0;i<max;++i)
{
if(events[i].data.fd==sockfd)
{
connectfd=accept(sockfd,NULL,NULL);
if(connectfd<0)
{
perror("accept error!\n");
continue;
}
printf("%d\n",count);
count++;
ev.events=EPOLLIN|EPOLLET;
ev.data.fd=connectfd;
epoll_ctl(epollfd,EPOLL_CTL_ADD,connectfd,&ev);
}
else
{
if(events[i].events&EPOLLIN)
{
int n=read(events[i].data.fd,buf,BUFFSIZE);
if(n==0)
{
epoll_ctl(epollfd,EPOLL_CTL_DEL,events[i].data.fd,NULL);
close(events[i].data.fd);
}
}
}
}
}
}服务器收到一个请求,计数器就加一并且输出。
客户端:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <pthread.h>
#define MAXTHREAD 512
void * startconnect(void *arg)
{
int sockfd;
char * ip=(char *)arg;
sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0)
{
perror(strerror(errno));
return;
}
struct sockaddr_in client;
memset(&client,0,sizeof(struct sockaddr));
client.sin_family=AF_INET;
client.sin_port=htons(8989);
inet_pton(AF_INET,ip,&client.sin_addr.s_addr);
//connect(sockfd,(struct sockaddr *)&client,sizeof(struct sockaddr));
if(connect(sockfd,(struct sockaddr *)&client,sizeof(struct sockaddr))<0)
{
perror(strerror(errno));
close(sockfd);
return NULL;
}
close(sockfd);
return NULL;
}
int main(int argc,char * argv[])
{
if(argc<1)
{
perror("too few arguments!\n");
return -1;
}
int sockfd;
sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0)
{
perror(strerror(errno));
return -1;
}
pthread_t threads[MAXTHREAD];
int i;
for(i=0;i<MAXTHREAD;++i)
threads[i]=pthread_create(threads+i,NULL,startconnect,argv[1]);
return 0;
}客户端用512个线程进行连接,客户端没有提示某个线程连接错误。执行的结果是服务器收到了480多个请求。
这是根据我个人的理解写的一个例子,有什么不对的地方请指正。
本文通过一个简单的测试案例,展示了epoll边界触发模式下可能出现的连接丢失问题。使用C语言编写了一个服务器端程序,该程序利用epoll进行事件监听,并采用边界触发模式。同时,还提供了一个客户端程序,用于并发地发起大量连接请求,以此来验证服务器端在边界触发模式下处理连接的能力。
3230





