多路复用并发模型 -- epoll
监控事件 events
EPOLLIN fd可读
EPOLLOUT fd可写
EPOLLPRI fd紧急数据可读
EPOLLERR fd发生错误
EPOLLHUP fd 被挂起
EPOLLONESHOT fd 只监控 1 次,监控完后自动删除
EPOLLLT epoll 工作模式,设置为 水平触发模式
EPOLLET epoll 工作模式,设置为 边缘触发模式
多路复用并发模型 -- epoll
epoll 工作模式
1)水平触发模式 (Level Triggered, 默认值)
事件发生时,应用程序可以不立即处理。
没有做处理,则下次epoll_wait 事件仍然被置位
2)边缘触发模式 (Edge Triggered)
事件发生时,应用程序必须处理
否则,这个事件会被丢弃
epoll工作在ET模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死
水平触发模式和边缘触发模式
水平触发监控的是状态: 有没有消息可读
边缘触发监控的是变换: 是不是由可读变成不可读,或者由不可读变成可读
#include<stdio.h> #include<unistd.h> #include<string.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h> #include <map> #include<sys/epoll.h> #include<sys/time.h> #define SRV_PORT 0xabcd #define DEAL_NUM 2 #define CONN_MAX 10000 int fd; int nConn = 0;//num of connection std::map<int, struct socket_in> g_fdmap; void epoll_process(int epfd,struct epoll_events,int cnt) { int i, iRet = 0;; int newfd; char szBuff[1000]; struct sockaddr_in addr; socklen_t addrlen = sizeof(addr); struct epoll_event ect; std::map<int struct socket_in>::iterator it; for (i = 0; i < cnt; ++i) { //Accept the message and process it if (evts[i].data.fd == STDIN_FILENO) { } //new client to connet else if (evts[i].data.fd == fd) { newfd = accept(fd, (struct sockaddr*)&addr, &addrlen); if (newfd < 0) { perror("Fail to accept!"); continue;; } if (nConn == CONN_MAX) { write(newfd, "Over limit!", 12); printf("Over connect..\n"); close(newfd); continue; } //normal operation g_fdmap.insert(std::make_pair(newfd, addr)); //add newfd to epfd for monitor evt.events = EPOLLIN; evt.data.fd = newfd; epoll_ctl(epfd, EPOLL_CTL_ADD, newfd, &evt); //reponse client write(newfd, "Welcome", 8); printf("\rNew Connect from %s[%d]\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); } //recviver client message else { memset(szBuff, 0, 1000); it = g_fdmap.find(evts[i].data.fd); if (it == g_fdmap.end()) { printf("\rUnknow client fd: %d\n", evts[i].data.fd); continue; } iRet=read(evts.data.fd, szBuff, 1000); if (iRet < 0) { perror("Fail to read!"); continue; } else if (iRet == 0) { printf("Disconnct from %s[%d]\n", inet_ntoa((it->second()).sin_addr), ntohs((it->second()).addr.sin_port)); //delete fd from epfd monitor epoll_ctl(epfd, EPOLL_CTL_DEL,evts[i].data.fd,evts+i); //erase fa from map g_fdmap.erase(it); } //normal operation else { printf("\rRecv from %s[%d]:%s\n", inet_ntoa((it->second()).sin_addr, ntohs((it->second()).addr.sin_port), szBuff); } } } return; } void StarEpoll() { int iRet; struct sockaddr_in addr; socklen_t addrlen = sizeof(addr); fd = socket(PF_INET, SOCK_STREAM, 0); if (fd < 0) { perror("Fail to socket!"); return; } addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(SRV_PORT); iRet = bind(fd, (struct sockaddr*)&addr, addrlen); if (iRet) { perror("Fail to bind!"); close(fd); return; } iRet = listen(fd, 100); if (iRet) { perror("Fail to listen!"); close(fd); return; } ///////////////init epollfd int epfd = epoll_create(1); if (epfd < 0) { perror("Fail to epoll_create!"); close(fd); return; } struct epoll_event evt; //add stdin evt.events = EPOLLIN; evt.data.fd =STDIN_FILENO; epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO); //add tcp server fd evt.events = EPOLLIN; evt.data.fd = fd; epoll_ctl(epfd, EPOLL_CTL_ADD, &evt); /////////////////////// struct epoll_event evts[DEAL_NUM]; int cnt; while (1) { cnt = epoll_wait(epfd, evts, DEAL_NUM, -1); if (cnt < 0) { perror("Fail to epoll_wait!"); break; } else if (cnt == 0) { //timeout continue; } else { epoll_process(epfd, evts, cnt); } } close(fd); return; } int main() { StarEpoll(); return 0; }