服务器二: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 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具