Linux高并发学习----一请求一线程/select/poll/epoll基本使用
2021/12/26 7:12:17
本文主要是介绍Linux高并发学习----一请求一线程/select/poll/epoll基本使用,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
1、概述
作为服务端,同时支持多用户连接是必然要求,在刚开始学习网络编程时,咱们所想到的几种常见用法如下:
1、一个请求对应一个线程:即给每一个新连接用户分配一个新线程,在该线程处理业务,这种情况显然只适用于很小规模连接的场景,毕竟线程资源是有限的,一般的pc能开到几百个线程就不错了。
2、select:能够将大量事件存入对应集合中,只需要遍历集合,处理不同类型事件即可,但是支持的socket连接有限,一般是1024。
3、poll:类似于select,但是处理事件时更加方便,不需要设置多个事件集合,且支持的socket连接较select也大幅提升。
4、epoll:让Linux成为服务器端开发主战场的得力功臣,支持单机百万并发连接,相较于select/poll,不需要对事件集合一一遍历(有些连接处于就绪态,没有事件触发),效率很高。
2 、一请求一线程
在这种情况下,每当accept一个连接,则创建一个新线程,在线程中处理用户事件即可,下面的代码省略了socket从创建到listen的阶段。
for(;;) { connfd = accept(listenfd, (struct sockaddr*)&client_adr, &client_adr_lenth); pthread_t threadId; pthread_create(&threadId, NULL, threadFunc, (void*)&connfd); } close(listenfd);
对应的线程入口函数如下:
void *threadFunc(void *args) { int cntFd = *(int *)args; if(cntFd > 0) printf("recv a connection! clientfd = %d\n", cntFd); else { printf("accept error!\n"); exit(1); } while (1) { int n = recv(cntFd, buf, 256, 0); if(n > 0) { printf("recv : %s\n", buf); write(cntFd, str, strlen(str)); } else { printf("对端fd = %d 断开连接!\n", cntFd); close(cntFd); break; } } }
3、select
同样省略socket从创建到listen的阶段,给出select示例代码如下:
fd_set rfds, rset, wfds, wset; FD_ZERO(&rfds); FD_SET(listenfd, &rfds); FD_ZERO(&wfds); int max_fd = listenfd; while (1) { rset = rfds; wset = wfds; // rset是读事件集合,wset是写事件集合,后续事件处理从对应集合中读取即可 int nready = select(max_fd+1, &rset, &wset, NULL, NULL); if (FD_ISSET(listenfd, &rset)) // 首先处理完成三次握手的连接 { struct sockaddr_in client; socklen_t len = sizeof(client); if ((connfd = accept(listenfd, (struct sockaddr *)&client, &len)) == -1) { printf("accept socket error: %s(errno: %d)\n", strerror(errno), errno); return 0; } FD_SET(connfd, &rfds); if (connfd > max_fd) max_fd = connfd; if (--nready == 0) continue; } int i = 0; for (i = listenfd + 1;i <= max_fd;i ++) { if (FD_ISSET(i, &rset)) { n = recv(i, buff, MAXLNE, 0); if (n > 0) { buff[n] = '\0'; printf("recv msg from client fd = %d: %s\n", i, buff); FD_SET(i, &wfds); send(i, buff, n, 0); } else if (n == 0) { FD_CLR(i, &rfds); printf("对端断开连接!fd = %d\n", i); close(i); } if (--nready == 0) break; } } }
4、poll
struct pollfd pollfds[MAX_POLL_SIZE]; // 存放发生的事件集合 pollfds[0].fd = listenfd; pollfds[0].events = POLLIN; pollfds[0].revents = 0; int maxfd = listenfd; for(int i = 1; i < MAX_POLL_SIZE; ++i) pollfds[i].fd = -1; while(1) { int nready = poll(pollfds, maxfd+1, -1); if(nready < 0) { if(errno == EINTR) continue; break; } else if(nready == 0) // 超时 continue; if(pollfds[0].revents & POLLIN) // 完成三次握手的连接 { struct sockaddr_in clientAddr; socklen_t len = sizeof(clientAddr); connfd = accept(listenfd, (struct sockaddr*)&clientAddr, &len); if(fcntl(connfd, F_SETFL, O_NONBLOCK) == -1) { printf("fcntl() fail!\n"); return -1; } pollfds[connfd].fd = connfd; pollfds[connfd].events = POLLIN; pollfds[connfd].revents = 0; if(connfd > maxfd) maxfd = connfd; printf("new connnection : %d accepted!\n", connfd); if(--nready == 0) continue; } for(int i = listenfd; i <= maxfd; ++i) { if(pollfds[i].revents & POLLIN) { // 有数据到达 int n = recv(i, buf, 256, 0); if(n == 0) { printf("对端 %d 断开连接!\n", i); pollfds[i].fd = -1; close(i); } else if(n < 0) { if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) { printf("异常错误! errno = %d:%s\n", errno, strerror(errno)); return -1; } } else { buf[n] = '\0'; printf("recv msg: %s\n", buf); send(i, buf, n, 0); } if(--nready == 0) break; } } }
5、epoll
int epollfd = epoll_create(1); struct epoll_event events[512], ev; ev.events = EPOLLIN; ev.data.fd = listenfd; epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &ev); while(1) { int nready = epoll_wait(epollfd, events, 512, -1); if(nready == -1) continue; for(int i = 0; i < nready; ++i) { int clientfd = events[i].data.fd; if(clientfd == listenfd) // 完成三次握手的连接 { struct sockaddr_in clientAdr; socklen_t len = sizeof(clientAdr); connfd = accept(clientfd, (struct sockaddr*)&clientAdr, &len); printf("new connection: %d\n", connfd); ev.data.fd = connfd; ev.events = EPOLLIN; epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &ev); } else { if(events[i].events & EPOLLIN) // 读事件 { printf("recv:\n"); int n = recv(clientfd, buf, 256, 0); if(n == 0) { printf("client fd = %d closed!\n", clientfd); ev.events = EPOLLIN; ev.data.fd = clientfd; epoll_ctl(epollfd, EPOLL_CTL_DEL, clientfd, &ev); close(clientfd); } else { buf[n] = '\0'; printf("%s\n", buf); send(clientfd, buf, n, 0); } } } } }
6、后续
本篇文章使用了几种最基本的支持多连接的情况,下一篇文章将介绍使用epoll的reactor写法。
代码中有问题的欢迎评论区留言哈!
这篇关于Linux高并发学习----一请求一线程/select/poll/epoll基本使用的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-18git仓库有更新,jenkins 自动触发拉代码怎么配置的?-icode9专业技术文章分享
- 2024-12-18Jenkins webhook 方式怎么配置指定的分支?-icode9专业技术文章分享
- 2024-12-13Linux C++项目实战入门教程
- 2024-12-13Linux C++编程项目实战入门教程
- 2024-12-11Linux部署Scrapy教程:新手入门指南
- 2024-12-11怎么将在本地创建的 Maven 仓库迁移到 Linux 服务器上?-icode9专业技术文章分享
- 2024-12-10Linux常用命令
- 2024-12-06谁看谁服! Linux 创始人对于进程和线程的理解是…
- 2024-12-04操作系统教程:新手入门及初级技巧详解
- 2024-12-04操作系统入门:新手必学指南