关于epoll替代select作为高性能服务器的事件通知机制的资料相当多,我就不在这里班门弄斧了,有兴趣的同学可以参考末尾的文献链接。
这里说明如下:
1.epoll是linux下高并发服务器的完美方案,因为是基于事件触发的,所以比select快的不只是一个数量级。
2.单线程epoll,触发量可达到15000,参见文献[4]
3.高性能server要使用非阻塞方式。
epoll服务器端
//compile: g++ -g epoll_server.cpp -o epoll_server //run: ./epoll_server // #include <sys/socket.h> #include <sys/epoll.h> #include <sys/sendfile.h> #include <sys/wait.h> #include <sys/stat.h> #include <netinet/in.h> #include <netinet/tcp.h> #include <arpa/inet.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <strings.h> #include <fcntl.h> #include <errno.h> #define MAX_EVENTS 10 #define LISTENQ 20 #define PORT 5000 //8080 //设置socket连接为非阻塞模式 void setnonblocking (int fd) { int opts; opts = fcntl (fd, F_GETFL); if (opts < 0) { perror ("fcntl(F_GETFL)\n"); exit (1); } opts = (opts | O_NONBLOCK); if (fcntl (fd, F_SETFL, opts) < 0) { perror ("fcntl(F_SETFL)\n"); exit (1); } } int main () { struct epoll_event ev, events[MAX_EVENTS]; int listenfd, connfd, nfds, epfd, sockfd, i, nread, n; struct sockaddr_in local, remote; socklen_t addrlen; char buf[BUFSIZ]; //创建listen socket if ((listenfd = socket (AF_INET, SOCK_STREAM, 0)) < 0) { perror ("sockfd\n"); exit (1); } setnonblocking (listenfd); bzero (&local, sizeof (local)); local.sin_family = AF_INET; local.sin_addr.s_addr = htonl (INADDR_ANY);; local.sin_port = htons (PORT); if (bind (listenfd, (struct sockaddr *) &local, sizeof (local)) < 0) { perror ("bind\n"); exit (1); } listen (listenfd, LISTENQ); epfd = epoll_create (MAX_EVENTS); if (epfd == -1) { perror ("epoll_create"); exit (EXIT_FAILURE); } ev.events = EPOLLIN; ev.data.fd = listenfd; if (epoll_ctl (epfd, EPOLL_CTL_ADD, listenfd, &ev) == -1) { perror ("epoll_ctl: listen_sock"); exit (EXIT_FAILURE); } for (;;) { nfds = epoll_wait (epfd, events, MAX_EVENTS, -1); if (nfds == -1) { perror ("epoll_wait error"); exit (EXIT_FAILURE); } for (i = 0; i < nfds; ++i) { sockfd = events[i].data.fd; if (sockfd == listenfd) { while ((connfd = accept (listenfd, (struct sockaddr *) &remote, &addrlen)) > 0) { char *ipaddr = inet_ntoa (remote.sin_addr); printf("accept a connection from [%s]\n", ipaddr); setnonblocking (connfd); //设置连接socket为非阻塞 ev.events = EPOLLIN | EPOLLET; //边沿触发要求套接字为非阻塞模式;水平触发可以是阻塞或非阻塞模式 ev.data.fd = connfd; if (epoll_ctl (epfd, EPOLL_CTL_ADD, connfd, &ev) == -1) { perror ("epoll_ctl: add"); exit (EXIT_FAILURE); } } if (connfd == -1) { if (errno != EAGAIN && errno != ECONNABORTED && errno != EPROTO && errno != EINTR) perror ("accept"); } continue; } if (events[i].events & EPOLLIN) { n = 0; while ((nread = read (sockfd, buf + n, BUFSIZ - 1)) > 0) { n += nread; } if (nread == -1 && errno != EAGAIN) { perror ("read error"); } printf("recv from client data [%s]\n", buf); ev.data.fd = sockfd; ev.events = events[i].events | EPOLLOUT; if (epoll_ctl (epfd, EPOLL_CTL_MOD, sockfd, &ev) == -1) { perror ("epoll_ctl: mod"); } } if (events[i].events & EPOLLOUT) { snprintf (buf, sizeof(buf), "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\nHello World", 11); int nwrite, data_size = strlen (buf); n = data_size; while (n > 0) { nwrite = write (sockfd, buf + data_size - n, n); if (nwrite < n) { if (nwrite == -1 && errno != EAGAIN) { perror ("write error"); } break; } n -= nwrite; } printf("send to client data [%s]\n", buf); close (sockfd); events[i].data.fd = -1; } } } close (epfd); close (listenfd); return 0; }
epoll客户端
//compile: g++ -g epoll_client.cpp -o epoll_client //run: ./epoll_client // #include <iostream> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/types.h> #include <sys/socket.h> using namespace std; #define PORT 5000 int main(int argc, char* argv[]) { int sockfd, on = 1; char buffer[512] = {0}; struct sockaddr_in servaddr; memset(&servaddr, 0, sizeof(servaddr)); if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){ cout << "create socket fail" << endl; return -1; } cout << "succeed to create client socket fd " << sockfd << endl; setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); cout << "set socket reuse by etsockopt" << endl; servaddr.sin_port = htons((short)PORT); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //此处更改epoll服务器地址 if(connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){ cout << "connect error" << endl; return -1; } cout << "succeed to connect epoll server " << endl; char target[] = "The Author: [email protected]"; memcpy(buffer, target, strlen(target)); int wlen = send(sockfd, buffer, strlen(buffer), 0); if(wlen <= 0) cout << " send data to server fail " << strerror(errno) << endl; cout << "send data to server on success, data: [" << buffer << "]"<< endl; memset(buffer, 0, sizeof(buffer)); int rlen = recv(sockfd, buffer, sizeof(buffer), 0); if(rlen <= 0) cout << " receive data from server fail " << strerror(errno) << endl; cout << "receive data from server on success, data: [" << buffer << "]" << endl; return 0; }
大家可以在上述框架上进一步修改.下面是运行图
下面的参考文献按照我认为的优先级递减排列
[1].http://www.cnblogs.com/aicro/archive/2012/12/27/2836170.html
[2].http://blog.csdn.net/hzhsan/article/details/23650465
[3].http://blog.csdn.net/ljx0305/article/details/4065058
[4].http://blog.csdn.net/win_lin/article/details/7843000
[5].http://blog.csdn.net/win_lin/article/details/7566466
[6].http://blog.csdn.net/qcghdy/article/details/22791077
[7].http://www.cnblogs.com/iTsihang/archive/2013/05/23/3095775.html
[8].http://www.cppblog.com/API/archive/2013/11/27/204456.html#204479