服务器四:多进程epoll

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

猜你喜欢

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