epoll ET模式服务器和客户端源码例子
關于epoll替代select作為高性能服務器的事件通知機制的資料相當多,我就不在這里班門弄斧了,有興趣的同學可以參考末尾的文獻鏈接。
這里說明如下:
1.epoll是linux下高并發服務器的完美方案,因為是基于事件觸發的,所以比select快的不只是一個數量級。
2.單線程epoll,觸發量可達到15000,參見文獻[4]
3.高性能server要使用非阻塞方式。
使用ET模型的時候,一定要注意,每次收到有效通知,然后讀取數據的時候,務必每次讀取干凈(讀到出錯為止)。當再次調用check(sockfd)的時候才能正確返回。 目前使用的epoll模型大多都是ET模式,socket都要設置為非阻塞的。 網上找了很多源碼例子,但是大多不理想,下面是我根據網上資料修改嘗試出的一個例子,供大家參考。后續更進一步地研究可以參考nginx,memcache,Apache traffic 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 socketif ((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 5000int 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: tao_627@aliyun.com";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
[9].http://blog.csdn.net/ljx0305/article/details/4065058總結
以上是生活随笔為你收集整理的epoll ET模式服务器和客户端源码例子的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: redisCommand接口的用法--利
- 下一篇: vim中删除dos格式文件中的^M号的方