用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);
}
上一篇: angularjs select 赋值 ng-options配置方法
下一篇: Makefile详解