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 }
服务器四:多进程epoll
猜你喜欢
转载自www.cnblogs.com/venjin/p/9187889.html
今日推荐
周排行