服务器二:epoll

  1 #include <unistd.h>
  2 #include <sys/types.h>
  3 #include <fcntl.h>
  4 #include <sys/socket.h>
  5 #include <netinet/in.h>
  6 #include <arpa/inet.h>
  7 #include <signal.h>
  8 #include <fcntl.h>
  9 #include <sys/wait.h>
 10 #include <sys/epoll.h>
 11 
 12 #include <stdlib.h>
 13 #include <stdio.h>
 14 #include <errno.h>
 15 #include <string.h>
 16 
 17 #include <vector>
 18 #include <algorithm>
 19 #include <iostream>
 20 
 21 typedef std::vector<struct epoll_event> EventList;
 22 
 23 #define ERR_EXIT(m) \
 24         do \
 25         { \
 26                 perror(m); \
 27                 exit(EXIT_FAILURE); \
 28         } while(0)
 29 
 30 int main(void)
 31 {
 32 //    TCP是全双工的信道, 可以看作两条单工信道, TCP连接两端的两个端点各负责一条. 当对端调用close时, 虽然本意是关闭整个两条信道,
 33 //    但本端只是收到FIN包. 按照TCP协议的语义, 表示对端只是关闭了其所负责的那一条单工信道, 仍然可以继续接收数据. 也就是说, 因为TCP协议的限制,
 34 //    一个端点无法获知对端的socket是调用了close还是shutdown.
 35 //    对一个已经收到FIN包的socket调用read方法,
 36 //    如果接收缓冲已空, 则返回0, 这就是常说的表示连接关闭. 但第一次对其调用write方法时, 如果发送缓冲没问题, 会返回正确写入(发送).
 37 //    但发送的报文会导致对端发送RST报文, 因为对端的socket已经调用了close, 完全关闭, 既不发送, 也不接收数据. 所以,
 38 //    第二次调用write方法(假设在收到RST之后), 会生成SIGPIPE信号, 导致进程退出.
 39 //    为了避免进程退出, 可以捕获SIGPIPE信号, 或者忽略它, 给它设置SIG_IGN信号处理函数:
 40 //    这样, 第二次调用write方法时, 会返回-1, 同时errno置为SIGPIPE. 程序便能知道对端已经关闭.
 41     signal(SIGPIPE, SIG_IGN);//防止进程退出
 42     //忽略SIGCHLD信号,这常用于并发服务器的性能的一个技巧
 43     //因为并发服务器常常fork很多子进程,子进程终结之后需要
 44     //服务器进程去wait清理资源。如果将此信号的处理方式设为
 45     //忽略,可让内核把僵尸子进程转交给init进程去处理,省去了
 46     //大量僵尸进程占用系统资源。(Linux Only)
 47     signal(SIGCHLD, SIG_IGN);
 48 
 49     int idlefd = open("/dev/null", O_RDONLY | O_CLOEXEC);
 50     int listenfd;
 51     //if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
 52     if ((listenfd = socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP)) < 0)
 53         ERR_EXIT("socket");
 54 
 55     struct sockaddr_in servaddr;
 56     memset(&servaddr, 0, sizeof(servaddr));
 57     servaddr.sin_family = AF_INET;
 58     servaddr.sin_port = htons(5188);
 59     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
 60 
 61     int on = 1;
 62     if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
 63         ERR_EXIT("setsockopt");
 64 
 65     if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
 66         ERR_EXIT("bind");
 67     if (listen(listenfd, SOMAXCONN) < 0)
 68         ERR_EXIT("listen");
 69 
 70     //create epoll object
 71     int epollfd;
 72     epollfd = epoll_create1(EPOLL_CLOEXEC);
 73 
 74     //add EPOLLIN event to epoll
 75     struct epoll_event event;
 76     event.data.fd = listenfd;
 77     event.events = EPOLLIN/* | EPOLLET*/;
 78     epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &event);
 79 
 80     EventList events(16);
 81     struct sockaddr_in peeraddr;
 82     socklen_t peerlen;
 83     int connfd;
 84 
 85     std::vector<int> clients;
 86 
 87     int nready;
 88     while (1)
 89     {
 90         //return active event
 91         nready = epoll_wait(epollfd, &*events.begin(), static_cast<int>(events.size()), -1);
 92         if (nready == -1)
 93         {
 94             if (errno == EINTR)
 95                 continue;
 96 
 97             ERR_EXIT("epoll_wait");
 98         }
 99         if (nready == 0)    // nothing happended
100             continue;
101 
102         //double capacity
103         if ((size_t)nready == events.size())
104             events.resize(events.size()*2);
105 
106         //treat all active event
107         for (int i = 0; i < nready; ++i)
108         {
109             //如果是主socket的事件的话,则表示有新连接进入了,进行新连接的处理
110             if (events[i].data.fd == listenfd)
111             {
112                 peerlen = sizeof(peeraddr);
113                 connfd = ::accept4(listenfd, (struct sockaddr*)&peeraddr,
114                         &peerlen, SOCK_NONBLOCK | SOCK_CLOEXEC);
115 
116                 if (connfd == -1)
117                 {
118                     //防止文件句柄超过最大数量
119                     if (errno == EMFILE)
120                     {
121                         close(idlefd);
122                         idlefd = accept(listenfd, NULL, NULL);
123                         close(idlefd);
124                         idlefd = open("/dev/null", O_RDONLY | O_CLOEXEC);
125                         continue;
126                     }
127                     else
128                         ERR_EXIT("accept4");
129                 }
130 
131 
132                 std::cout<<"ip="<<inet_ntoa(peeraddr.sin_addr)<<
133                     " port="<<ntohs(peeraddr.sin_port)<<std::endl;
134 
135                 clients.push_back(connfd);
136 
137                 event.data.fd = connfd;
138                 event.events = EPOLLIN/* | EPOLLET*/;
139                 epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &event);
140             }
141             else if (events[i].events & EPOLLIN)
142             {
143                 connfd = events[i].data.fd;
144                 if (connfd < 0)
145                     continue;
146 
147                 char buf[1024] = {0};
148                 int ret = read(connfd, buf, 1024);
149                 if (ret == -1)
150                     ERR_EXIT("read");
151                 if (ret == 0)
152                 {
153                     std::cout<<"client close"<<std::endl;
154                     close(connfd);
155                     event = events[i];
156                     epoll_ctl(epollfd, EPOLL_CTL_DEL, connfd, &event);
157                     clients.erase(std::remove(clients.begin(), clients.end(), connfd), clients.end());
158                     continue;
159                 }
160 
161                 std::cout<<buf;
162                 write(connfd, buf, strlen(buf));
163             }
164 
165         }
166     }
167 
168     return 0;
169 }

猜你喜欢

转载自www.cnblogs.com/venjin/p/9175114.html