欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

用c语言实现的epoll服务器

程序员文章站 2022-05-09 08:07:59
...

在linux后台开发实战操作中,epoll服务器,是必备技能。

我今天就写了一个最简单的TCP server,你稍微改改就可以用于简单的数据通信。

如果要实现别的功能,比如ET,非阻塞,数据缓存,那都要围绕读写函数改了。

主要的系统API:

int epoll_create(int size);

int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

以下是代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/socket.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <sys/epoll.h>

#define SERVER_IPADDR "0.0.0.0"

#define SERVER_PORT "1122"

#define EV_READ EPOLLIN

#define EV_WRITE EPOLLOUT

#define MAX_EVENT_NUMS 1024

static int clifds[MAX_EVENT_NUMS];


int startListenSocket(const char *ipaddr,const char *port){
	int sck = socket(AF_INET,SOCK_STREAM,0);//IPV4
	if(sck < 0){
		perror("socket");
		exit(EXIT_FAILURE);
	}
	struct sockaddr_in addr;
	bzero(&addr,sizeof(addr));
	if(ipaddr)
		addr.sin_addr.s_addr = inet_addr(ipaddr);
	else
		addr.sin_addr.s_addr = htonl(INADDR_ANY);
	addr.sin_family = AF_INET;
	addr.sin_port = htons(atoi(port));

	int val = 1;
	setsockopt(sck,SOL_SOCKET,SO_REUSEADDR,&val,sizeof(val));
	
	if(bind(sck,(struct sockaddr*)&addr,sizeof(addr)) < 0){
		perror("bind");
		close(sck);
		exit(EXIT_FAILURE);
	}	

	if(listen(sck,20) < 0){
		perror("listen");
		close(sck);
		exit(EXIT_FAILURE);	
	}
	return sck;
}

int epoll_event_add(int epfd,int fd,int event){
	struct epoll_event ev;
	ev.data.fd = fd;
	ev.events = event;
	return epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&ev);
}

int epoll_event_del(int epfd,int fd,int event){
	struct epoll_event ev;
	ev.data.fd = fd;
	ev.events = event;
	return epoll_ctl(epfd,EPOLL_CTL_DEL,fd,&ev);
}



int do_accept(int listenSocket,int epfd){
	struct sockaddr_in cliaddr;
	bzero(&cliaddr,sizeof(cliaddr));
	socklen_t addrlen = sizeof(struct sockaddr_in);
	int fd = accept(listenSocket,(struct sockaddr*)&cliaddr,&addrlen);
	if(fd < 0){
		perror("accept");
		return (-1);
	}
	printf("Accept new client <%s:%d>\n",inet_ntoa(cliaddr.sin_addr),\
	ntohs(cliaddr.sin_port));
	clifds[fd] = 1;//设置有效状态
	epoll_event_add(epfd,fd,EV_READ);
	return 0;
}

int recv_client(int epfd,struct epoll_event*evs,int i){
	char buf[1024]={0};
	bzero(buf,sizeof(buf));
	int n = read(evs[i].data.fd,buf,1024);
	if(n > 0){
		printf("Recv<fd-%d>:%s\n",evs[i].data.fd,buf);
		//处理数据,这里可以做你要做的事情
		if(strncmp(buf,"exit",4) == 0){
			return (-1);
		}
		else{
			write(evs[i].data.fd,"From server:\n",strlen("From server:\n"));
			write(evs[i].data.fd,buf,n);
		}
	}
	//客户端断开
	if(n == 0){
		printf("Client <%d> close\n",evs[i].data.fd);
		epoll_event_del(epfd,evs[i].data.fd,EPOLLIN);
		close(evs[i].data.fd);
		clifds[evs[i].data.fd] = -1;//
	}
	return 0;
}


int main(int argc,char *argv[]){
	int listSocket = startListenSocket(SERVER_IPADDR,SERVER_PORT);
	int epfd = epoll_create(1024);
	if(epfd < 0){
		perror("epoll");
		close(listSocket);
		exit(EXIT_FAILURE);
	}
	epoll_event_add(epfd,listSocket,EV_READ);
	struct epoll_event evs[MAX_EVENT_NUMS];
	int i = 0;
	memset(clifds,-1,sizeof(clifds));
	do{
		int n = epoll_wait(epfd,evs,MAX_EVENT_NUMS,-1);
		if(n == 0){
			printf("epoll_wait timeout\n");
			continue;
		}
		if(n < 0){
			perror("epoll_eait");
			if(errno == EAGAIN || errno==EINTR){
				continue;
			}
			break;			
		}
		
		//这里实际是一个reactor
		for(i=0;i < n;++i){
			if(evs[i].events & EV_READ){			
				//处理监听套接字的数据
				if(evs[i].data.fd == listSocket){
					if(do_accept(listSocket,epfd) < 0){
						perror("do_accept");
					}
				}
				else{
					//读取客户端的数据
					if(recv_client(epfd,evs,i) <0){
						goto EXIT;
					}	
				}
			}					
		}
	}while(1);
EXIT:
	for(i=0;i<MAX_EVENT_NUMS;++i){
		if(clifds[i] >= 0 ){
			printf("Close sockfd <%d>\n",i);
			close(i);
		}
	}
	close(listSocket);
	exit(EXIT_SUCCESS);
}