epoll中listen socket的触发模式选择

本文通过一个简单的测试案例,展示了epoll边界触发模式下可能出现的连接丢失问题。使用C语言编写了一个服务器端程序,该程序利用epoll进行事件监听,并采用边界触发模式。同时,还提供了一个客户端程序,用于并发地发起大量连接请求,以此来验证服务器端在边界触发模式下处理连接的能力。

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多个请求。

这是根据我个人的理解写的一个例子,有什么不对的地方请指正。

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值