简单的epoll模型
程序员文章站
2024-03-23 10:18:16
...
水平触发模式
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <sys/epoll.h>
int main(int argc,const char* argv[]){
if(argc < 2){
printf("eg: ./a.out port\n");
exit(1);
}
struct sockaddr_in serv_addr;
socklen_t serv_len = sizeof(serv_addr);
int port = atoi(argv[1]);
//创建套接字
int lfd = socket(AF_INET,SOCK_STREAM,0);
//初始化socket_in
memset(&serv_addr,0,serv_len);
serv_addr.sin_family = AF_INET; //地址族
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); //监听本机所有的IP
serv_addr.sin_port = htons(port); //设置端口
//绑定端口和IP
bind(lfd,(struct sockaddr*)&serv_addr,serv_len);
//设置同时监听的最大个数
listen(lfd,36);
printf("start accept ...\n");
struct sockaddr_in client_addr;
socklen_t cli_len = sizeof(client_addr);
//创建epoll树根节点
int epfd = epoll_create(2000);
//初始化epoll树
struct epoll_event ev;
ev.events = EPOLLIN; //边沿模式;
ev.data.fd = lfd;
epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&ev);
struct epoll_event all[2000];
while(1){
//使用epoll通知内核fd文件IO检测
int ret = epoll_wait(epfd,all,sizeof(all)/sizeof(all[0]),-1);
int i;
for(i=0;i<ret;++i){
//遍历all数组中的前ret个元素
int fd = all[i].data.fd;
//判断是否有新连接
if(fd == lfd){
//有新连接
int cfd = accept(lfd,(struct sockaddr*)&client_addr,&cli_len);
if(cfd == -1){
perror("accept error");
exit(-1);
}
//将新得到的cfd上epoll树
ev.events = EPOLLIN;
ev.data.fd = cfd;
epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&ev);
//打印客户端信息
char ip[64] = {0};
printf("New Client IP: %s, Port: %d\n",
inet_ntop(AF_INET,&client_addr.sin_addr.s_addr,ip,sizeof(ip)),
ntohs(client_addr.sin_port));
}else{
//处理已连接的客户端发过来的数据
if(!all[i].events & EPOLLIN){
//如果没有读的操作那么跳过
continue;
}
//读数据
char buf[1024] = {0};
int len = recv(fd,buf,sizeof(buf),0);
if(len == -1){
perror("recv error");
exit(1);
}else if(len ==0){
//客户端关闭
printf("client disconnected...\n");
//下树
ret = epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);
if(ret == -1){
perror("epoll_ctl - del error");
exit(1);
}
close(fd);
}else{
//正常情况
printf("recv buf: %s\n",buf);
//回给客户端数据
write(fd,buf,len);
}
}
}
}
close(lfd);
return 0;
}
边沿模式
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <sys/epoll.h>
int main(int argc,const char* argv[]){
if(argc < 2){
printf("eg: ./a.out port\n");
exit(1);
}
struct sockaddr_in serv_addr;
socklen_t serv_len = sizeof(serv_addr);
int port = atoi(argv[1]);
//创建套接字
int lfd = socket(AF_INET,SOCK_STREAM,0);
//初始化socket_in
memset(&serv_addr,0,serv_len);
serv_addr.sin_family = AF_INET; //地址族
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); //监听本机所有的IP
serv_addr.sin_port = htons(port); //设置端口
//绑定端口和IP
bind(lfd,(struct sockaddr*)&serv_addr,serv_len);
//设置同时监听的最大个数
listen(lfd,36);
printf("start accept ...\n");
struct sockaddr_in client_addr;
socklen_t cli_len = sizeof(client_addr);
//创建epoll树根节点
int epfd = epoll_create(2000);
//初始化epoll树
struct epoll_event ev;
ev.events = EPOLLIN | EPOLLET; //边沿模式;
ev.data.fd = lfd;
epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&ev);
struct epoll_event all[2000];
while(1){
//使用epoll通知内核fd文件IO检测
int ret = epoll_wait(epfd,all,sizeof(all)/sizeof(all[0]),-1);
int i;
for(i=0;i<ret;++i){
//遍历all数组中的前ret个元素
int fd = all[i].data.fd;
//判断是否有新连接
if(fd == lfd){
//有新连接
int cfd = accept(lfd,(struct sockaddr*)&client_addr,&cli_len);
if(cfd == -1){
perror("accept error");
exit(1);
}
//将新得到的cfd上epoll树
ev.events = EPOLLIN | EPOLLET; //边沿模式;
ev.data.fd = cfd;
epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&ev);
//打印客户端信息
char ip[64] = {0};
printf("New Client IP: %s, Port: %d\n",
inet_ntop(AF_INET,&client_addr.sin_addr.s_addr,ip,sizeof(ip)),
ntohs(client_addr.sin_port));
}else{
//处理已连接的客户端发过来的数据
if(!all[i].events & EPOLLIN){
//如果没有读的操作那么跳过
continue;
}
//读数据
char buf[1024] = {0};
int len = recv(fd,buf,sizeof(buf),0);
if(len == -1){
perror("recv error");
exit(-1);
}else if(len ==0){
//客户端关闭
printf("client disconnected...\n");
//下树
ret = epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);
if(ret == -1){
perror("epoll_ctl - del error");
exit(1);
}
close(fd);
}else{
//正常情况
printf("recv buf: %s\n",buf);
//回给客户端数据
write(fd,buf,len);
}
}
}
}
close(lfd);
return 0;
}
边沿非阻塞模式
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <errno.h>
int main(int argc,const char* argv[]){
if(argc < 2){
printf("eg: ./a.out port\n");
exit(1);
}
struct sockaddr_in serv_addr;
socklen_t serv_len = sizeof(serv_addr);
int port = atoi(argv[1]);
//创建套接字
int lfd = socket(AF_INET,SOCK_STREAM,0);
//初始化socket_in
memset(&serv_addr,0,serv_len);
serv_addr.sin_family = AF_INET; //地址族
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); //监听本机所有的IP
serv_addr.sin_port = htons(port); //设置端口
//绑定端口和IP
bind(lfd,(struct sockaddr*)&serv_addr,serv_len);
//设置同时监听的最大个数
listen(lfd,36);
printf("start accept ...\n");
struct sockaddr_in client_addr;
socklen_t cli_len = sizeof(client_addr);
//创建epoll树根节点
int epfd = epoll_create(2000);
//初始化epoll树
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = lfd;
epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&ev);
struct epoll_event all[2000];
while(1){
//使用epoll通知内核fd文件IO检测
int ret = epoll_wait(epfd,all,sizeof(all)/sizeof(all[0]),-1);
int i;
for(i=0;i<ret;++i){
//遍历all数组中的前ret个元素
int fd = all[i].data.fd;
//判断是否有新连接
if(fd == lfd){
//有新连接
int cfd = accept(lfd,(struct sockaddr*)&client_addr,&cli_len);
if(cfd == -1){
perror("accept error");
exit(-1);
}
//设置cfd为非阻塞模式
int flag = fcntl(cfd,F_GETFL);
flag |= O_NONBLOCK;
fcntl(cfd,F_SETFL,flag);
//将新得到的cfd上epoll树
ev.events = EPOLLIN | EPOLLET; //边沿模式
ev.data.fd = cfd;
epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&ev);
//打印客户端信息
char ip[64] = {0};
printf("New Client IP: %s, Port: %d\n",
inet_ntop(AF_INET,&client_addr.sin_addr.s_addr,ip,sizeof(ip)),
ntohs(client_addr.sin_port));
}else{
//处理已连接的客户端发过来的数据
if(!all[i].events & EPOLLIN){
//如果没有读的操作那么跳过
continue;
}
//读数据
char buf[1024] = {0};
int len;
while((len = recv(fd,buf,sizeof(buf),0))>0){
write(STDOUT_FILENO,buf,len);
//发送给客户端
send(fd,buf,len,0);
}
if(len == -1){
if(errno == EAGAIN){
printf("\n缓冲区读完\n");
}else{
perror("recv error");
exit(1);
}
}else if(len ==0){
//客户端关闭
printf("client disconnected...\n");
//下树
ret = epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);
if(ret == -1){
perror("epoll_ctl - del error");
exit(1);
}
close(fd);
}
}
}
}
close(lfd);
return 0;
}
可以使用nc进行测试
上一篇: Makefile与宏
下一篇: 依赖性任务处理