当前位置: 首页 > news >正文

相册模版网站图片展示投资公司名字起名大全

相册模版网站图片展示,投资公司名字起名大全,深圳企业建站设计公司,手机视频网站建站目录 一、Select 1、函数 API 2、使用限制 3、使用 Demo 二、Poll 三、epoll 0、 实现原理 1、函数 API 2、简单代码模板 3、LT/ET 使用过程 #xff08;1#xff09;LT 水平触发 #xff08;2#xff09;ET边沿触发 4、使用 Demo 四、参考链接 一、Select 在…目录 一、Select 1、函数 API 2、使用限制 3、使用 Demo 二、Poll 三、epoll 0、 实现原理 1、函数 API 2、简单代码模板 3、LT/ET 使用过程 1LT 水平触发 2ET边沿触发 4、使用 Demo 四、参考链接 一、Select 在 Linux 中select 就是一种经典的 I/O 复用机制。它允许服务器在一个线程内监控多个 I/O 事件比如多个客户端的连接状态。当服务器调用 select()它会依次“询问”每个连接是否有事件发生如果有事件发生了就立即处理。这样服务器不需要为每个连接创建线程使用单线程就可以服务于多个客户端从而节省了资源提升了效率。 1、函数 API 在实际使用 select 时我们会用到几个重要的函数和宏分别是 select() 本身以及操作 fd_set 结构的 FD_ZERO、FD_SET、FD_CLR、FD_ISSET 等宏函数。 #include sys/select.h /* select() 是 I/O 复用的核心函数用来等待多个文件描述符的状态变化。参数说明 nfds 要监控的文件描述符的数量通常是 fd_set 中最大的文件描述符值加 1。readfds 监控是否有数据可读的文件描述符集合。writefds 监控是否有数据可写的文件描述符集合。exceptfds监控异常事件的文件描述符集合。timeout 超时时间NULL 表示无限等待超时后 select 返回 0。返回值成功时返回就绪的文件描述符的总数。出错时返回 -1并设置 errno 以指示错误类型。 */ int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);/*在 select 中我们使用 fd_set 结构来标记哪些文件描述符需要被监控。fd_set 是一个位数组bitmap每个位代表一个文件描述符的位置。如果某个位被设置为 1表示我们希望 select 监控这个文件描述符。这里有几个重要的宏函数用于操作 fd_set。 */ // 将 fd_set 清空所有位清零。 FD_ZERO(fd_set) // 将指定的文件描述符 fd 加入 fd_set即把 fd_set 中 fd 的位设置为 1。 FD_SET(fd, fd_set) // 将指定的文件描述符 fd 从 fd_set 中移除即把 fd_set 中 fd 的位清零。 FD_CLR(fd, fd_set) // 检查 fd_set 中指定的文件描述符 fd 是否被设置为 1若为 1 表示该文件描述符有事件发生。 FD_ISSET(fd, fd_set) 2、使用限制 连接数限制select 在大部分系统中最多支持 1024 个连接如果 fd 并发特别多可以考虑 poll 或 epoll强烈推荐更适合高并发场景。 函数返回select()返回 IO 就绪的 fd 个数而且参数 fd_set 将被刷新只记录准备就绪的 IO 的 fd未就绪的 fd 将被移除。这块很容易混淆若 fd_set 不是一次性的建议在执行 select 之前进行备份每次执行 select 时使用临时变量传参。 # 1、监听 fd 集合3、4、5内核空间 fd_set 结构 ------------------------ | 0 | 0 | 0 | 1 | 1 | 1 | 0 |...| ------------------------0 1 2 3 4 5 6 ... (内核监控文件描述符 3、4、5 的状态)# 2、执行 select 后只有 fd 4 准备就绪 # 则其余 fd 在 fd_set 中全部被剔除置为 0内核空间 fd_set 更新 ------------------------ | 0 | 0 | 0 | 0 | 1 | 0 | 0 |...| ------------------------0 1 2 3 4 5 6 ... (仅文件描述符 4 发生事件保留 1) 灵活阻塞select 本身是阻塞的也可传参 timeval 变量设置阻塞事件该时间可以根据业务场景合理安排。若时间太小则浪费 CPU 资源CPU 会无故的频繁切换内核态和用户态若时间太长又可能无法及时处理 IO 时间。 3、使用 Demo 下面代码实现了基于 select 的多并发服务器。 int tcp_Server_Select(int argc, char **args) {char server_ip[MAX_IP_LENGTH] 127.0.0.1;uint16_t server_port 8088;// 可自定义服务器绑定的 IP 与 端口if ( argc 1 ){strcpy(server_ip, args[1]);}if ( argc 2 ){server_port atoi(args[2]);}// 记录客户端 fd int clients_fd[FD_SETSIZE - 2];int max_fd -1, clients_count -1;// select 监听的 fd 列表其中 set_tmp 是负责传参poll_set 负责全局fd_set set_tmp, poll_set;for ( int i 0; i FD_SETSIZE - 2; i ){clients_fd[i] -1;}//创建 TCP 监听套接字int listen_fd socket(AF_INET, SOCK_STREAM, 0);if ( listen_fd 0 ){log_error(Create Socket fd Failed);printf(Create Server FD Failed\n);return FAILURE;}//服务器端口复用int yes 1;setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, yes, sizeof(yes));//给服务器 socket 绑定 ip 和端口信息struct sockaddr_in server;server.sin_family AF_INET;server.sin_port htons(server_port);server.sin_addr.s_addr inet_addr(server_ip);int result bind(listen_fd, (struct sockaddr *)server, sizeof(server));if (result -1){log_error(Failed to bind Server Net Address);printf(Failed to bind Server Net Address);return FAILURE;}// 调用listenlisten(listen_fd, 10);// select fd_set 置空FD_ZERO(poll_set);// 将服务器的监听 fd 也添加到 fd_set 中负责监听是否有新的客户端接入FD_SET(listen_fd, poll_set);max_fd listen_fd;printf(TCP Server Listen On %s:%hu with fd %d\n, server_ip, server_port, listen_fd);while(1){// tmp 变量只在本次循环有效所以需要使用 poll_set 保存变量每次循环开始重新赋值。set_tmp poll_set;int ready_count select(max_fd 1, set_tmp, NULL, NULL, NULL);if ( ready_count 0 ){printf(Failed to execute select\n);log_error(Failed to execute select);break;}else if ( ready_count 0 ){// 先检查是否有新的 TCP 客户端接入printf(ready count %d\n, ready_count);if ( FD_ISSET(listen_fd, set_tmp) ){struct sockaddr_in client;socklen_t len sizeof(client);// accept 调用一次接入一个 tcp 客户端int client_fd accept(listen_fd, (struct sockaddr *)client, len);for ( int i 0; i FD_SETSIZE - 2; i ){// 记录新的客户端 fdif ( clients_fd[i] -1 ){clients_fd[i] client_fd;if ( clients_count i 1 ){clients_count i 1;}printf(client fd %d --- i %d --- clients_count %d\n, client_fd, i, clients_count);break;}}log_info(FD SetSize %d, FD_SETSIZE);if ( clients_count FD_SETSIZE - 1 ){// 若未达到连接边界则将新的客户端 fd 添加到监听集合中FD_SET(client_fd, poll_set);max_fd client_fd max_fd? client_fd: max_fd;//输出客户端信息char ip[MAX_IP_LENGTH] ;unsigned short port ntohs(client.sin_port);inet_ntop(AF_INET, client.sin_addr.s_addr, ip, MAX_IP_LENGTH);printf(client %s is connected %hu port\n, ip, port);log_info(client %s is connected %hu port, ip, port);}else{printf(Number of Clients reaches max limit\n);}ready_count--;}// 处理客户端 IO 事件for ( int i 0; i clients_count ready_count 0; i ){int client_fd clients_fd[i];if ( client_fd 0 ){continue;}// 若该客户端准备就绪则执行 recv 接受消息if ( FD_ISSET(client_fd, set_tmp) ){printf(client fd %d with i %d\n, client_fd, i);char msg[MAX_MSG_LENGTH] ;char msg_res[MAX_MSG_LENGTH] Recevied Successfully;int len recv(client_fd, msg, sizeof(msg), 0);// 异常情况将剔除客户端if ( len 0 ){printf(Release Fd %d\n, client_fd);close(client_fd);clients_fd[i] -1;FD_CLR(client_fd, poll_set);}else{printf(TCP Client Send: %s\n, msg);if ( send(client_fd, msg_res, strlen(msg_res), 0) 0 ){printf(---- Response Successfully With %s\n\n, msg_res);}to_lower_case(msg);//printf(--%s--\n, msg);// 客户端主动退出if ( !strcmp(exit, msg) ) {close(client_fd);clients_fd[i] -1;FD_CLR(client_fd, poll_set);printf(Close Socket FD %d\n, client_fd);}}--ready_count;}}}}close(listen_fd);return 0; } 二、Poll poll 是 select 的一种改进版本它消除了 select 的文件描述符数量限制API 函数使用起来稍有不同。poll 函数与 select 原理相似也是一种基于轮询的 I/O 多路复用机制它通过一个 struct pollfd 结构体数组来管理多个文件描述符。 #include poll.h /* Type used for the number of file descriptors. */ typedef unsigned long int nfds_t;struct pollfd {int fd; // 文件描述符short events; // 监听事件short revents; // 就绪事件 };/*fds: 监听的 fd 列表nfds监听的 fd 数量timeout监听阻塞超时时间 0 永远等待0 立即返回 0 等待的毫秒数 */ int poll(struct pollfd *fds, nfds_t nfds, int timeout); // return表示此时有多少个监控的描述符就绪若超时则为0出错为-1。 Event 类型如下所示感兴趣的可以看下 poll 和 select 函数一样两者都需要遍历整个文件描述符集合来检查状态因此性能都会随着文件描述符数量的增加而线性下降。 poll 与 select 的不同之处 1poll 函数采用链表的方式替代原来 select 中 fd_set 结构因此可监听文件描述符数量不受限在处理大量文件描述符时可能更具优势。 2poll 函数返回后可以通过 pollfd 结构中的内容进行处理就绪文件描述符相比 select 效率要高。避免了 select 需要重置文件描述符集合的问题。 3需要维护一个 struct pollfd 结构体数组这可能会增加一些编程复杂性。 poll 的使用示例如下 #include stropts.h #include poll.h ... struct pollfd fds[2]; int timeout_msecs 500; int ret; int i;/* Open STREAMS device. */ fds[0].fd open(/dev/dev0, ...); fds[1].fd open(/dev/dev1, ...); fds[0].events POLLOUT | POLLWRBAND; fds[1].events POLLOUT | POLLWRBAND;ret poll(fds, 2, timeout_msecs);if (ret 0) {/* An event on one of the fds has occurred. */for ( i0; i 2; i ) {if (fds[i].revents POLLWRBAND) {/* Priority data may be written on device number i. */ ...}if (fds[i].revents POLLOUT) {/* Data may be written on device number i. */ ...}if (fds[i].revents POLLHUP) {/* A hangup has occurred on device number i. */ ...}} } 三、epoll 重头戏来了下面介绍 linux 中的高并发 IO 复用 epoll很多服务器例如 nginx部署在 linux 中时都会使用 epoll 机制实现该并发 IO 操作避免阻塞。 0、 实现原理 epoll 将“维护等待队列”和“阻塞进程”两个步骤分开。先用epoll_ctl函数维护监听队列再调用epoll_wait函数阻塞进程。这种设计提高了效率特别是在需要监视的 socket 相对固定的场景下。 在内核中epoll 使用红黑树来跟踪所有待检测的文件描述符。红黑树是一种高效的数据结构时间复杂度O(logN)支持快速查找、插入和删除操作。这使得 epoll 能够高效地管理大量文件描述符。 epoll 采用事件驱动的方式仅在文件描述符状态发生变化时才会通知应用程序。这避免了每次遍历整个文件描述符集合的问题从而提高了性能。epoll 使用一个双向链表来记录就绪事件在执行 epoll_ctl 的 add 操作时不仅将文件描述符放到红黑树上而且也注册了回调函数内核在检测到某文件描述符可读/可写时会调用回调函数将该文件描述符放在就绪链表中。用户调用epoll_wait时只需检查这个列表是否有存在注册的事件红黑树即可避免了遍历所有文件描述符。 1、函数 API #include sys/epoll.h/**/ struct epoll_event {uint32_t events; /* 指定要监听的事件类型 */epoll_data_t data; /* 用户数据变量 */ } __EPOLL_PACKED;/* epoll_data_t是一个共用体其 4 个成员中使用最多的是 fd它指定事件所从属的目标文件描述符。ptr成员可以用来指定与fd相关的用户数据。但由于epoll_data_t是一个共用体我们不能同时使用其ptr成员和fd成员因此如果要将文件描述符和用户数据关联起来以实现快速的数据访问只能放弃使用epoll_data_t的fd成员而在ptr指向的用户数据中包含fd。 */ typedef union epoll_data {void *ptr; // 指定与fd相关的用户数据int fd; // 指定事件所从属的目标文件描述符uint32_t u32;uint64_t u64; } epoll_data_t;/*创建 epoll 实例, 并返回该实例的 fd。该函数会在内核中新建红黑树用于存储 epoll_ctl 管理的 fd还会新建双向链表用于记录已就绪的 fd。需要注意在使用完 epoll 后必须调用 close() 关闭该 fd否则会浪费描述符资源。返回值: 成功时返回一个文件描述符非负整数失败时返回 -1 并设置 errno。 */ int epoll_create1(int flags);/*添加、修改或删除监听的文件描述符参数:epfd: epoll 实例的文件描述符。op: 操作类型可以是以下之一EPOLL_CTL_ADD: 注册新的文件描述符到 epoll 实例中。EPOLL_CTL_MOD: 修改已注册的文件描述符的事件。EPOLL_CTL_DEL: 从 epoll 实例中删除文件描述符。fd: 需要监听的文件描述符。event: 指向 epoll_event 结构的指针用于指定事件和用户数据。返回值: 成功时返回 0失败时返回 -1 并设置 errno。 */ int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);/*等待事件发生当事件发生时会将对应的 fd 添加到 epoll 就绪队列中。参数:epfd: epoll 实例的文件描述符。events: 用于存储发生事件的数组。maxevents: 数组的最大长度。timeout: 超时时间毫秒。如果为 -1则无限等待如果为 0则立即返回。返回值: 成功时返回就绪的文件描述符数量失败时返回 -1 并设置 errno。 */ int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); Event 类型 EPOLLIN 表示对应的文件描述符上有数据可读 EPOLLOUT 表示对应的文件描述符上可以写入数据 EPOLLRDHUP 表示对端已经关闭连接或者关闭了写操作端的写入 EPOLLPRI 表示有紧急数据可读 EPOLLERR 表示发生错误 EPOLLHUP 表示文件描述符被挂起 EPOLLET 表示将 epoll 设置为边缘触发模式。在边缘触发模式下事件只有在状态发生变化时才会报告一次而不是像水平触发模式那样只要条件满足就持续报告。 EPOLLONESHOT 表示将事件设置为一次性事件。设置了这个标志后当事件处理完后epoll 会自动删除该事件无需再次手动调用 epoll_ctl 删除。 2、简单代码模板 创建epoll实例通过epoll_create函数创建一个epoll对象。维护监听列表使用epoll_ctl函数添加、删除或修改需要监视的文件描述符。接收数据当文件描述符收到数据后中断程序会操作epoll对象而不是直接操作进程。阻塞和唤醒进程当进程运行到epoll_wait时内核会将进程放入epoll对象的等待队列中阻塞进程。当文件描述符接收到数据中断程序一方面修改就绪列表另一方面唤醒epoll等待队列中的进程 #include sys/epoll.h #include unistd.h #include fcntl.h #include stdio.h #include stdlib.h #include string.h #include errno.h#define MAX_EVENTS 10int main() {int epoll_fd epoll_create1(0);if (epoll_fd -1) {perror(epoll_create1);exit(EXIT_FAILURE);}struct epoll_event event;struct epoll_event events[MAX_EVENTS];int listen_sock /* ... */; // 初始化监听套接字event.data.fd listen_sock;event.events EPOLLIN;if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_sock, event) -1) {perror(epoll_ctl);exit(EXIT_FAILURE);}while (1) {int nfds epoll_wait(epoll_fd, events, MAX_EVENTS, -1);if (nfds -1) {perror(epoll_wait);exit(EXIT_FAILURE);}for (int i 0; i nfds; i) {if (events[i].data.fd listen_sock) {// 处理新连接} else {// 处理现有连接的数据}}}close(epoll_fd);return 0; } 3、LT/ET 使用过程 摘自Linux下的I/O复用技术 — epoll如何使用epoll_create、epoll_ctl、epoll_wait 以及 LT/ET 使用过程解析_主动去触发epoll事件-CSDN博客https://blog.csdn.net/JMW1407/article/details/107963618 1LT 水平触发 Level Triggered socket接收缓冲区不为空 说明有数据可读 读事件一直触发socket发送缓冲区不满 说明可以继续写入数据 写事件一直触发符合思维习惯epoll_wait返回的事件就是socket的状态 LT 处理过程 accept 一个连接添加到 epoll 中监听 EPOLLIN 事件.当 EPOLLIN 事件到达时读取 fd 中的数据并处理 .当需要写出数据时把数据 write 到 fd 中如果数据较大无法一次性写出那么在 epoll 中监听EPOLLOUT 事件 。当 EPOLLOUT 事件到达时继续把数据 write 到 fd 中如果数据写出完毕那么在 epoll 中关闭EPOLLOUT 事件。 //LT模式的工作流程 void lt( epoll_event* events, int number, int epollfd, int listenfd ) {char buf[ BUFFER_SIZE ];for ( int i 0; i number; i ){int sockfd events[i].data.fd;if ( sockfd listenfd ){struct sockaddr_in client_address;socklen_t client_addrlength sizeof( client_address );int connfd accept( listenfd, ( struct sockaddr* )client_address, client_addrlength );addfd( epollfd, connfd, false );}else if ( events[i].events EPOLLIN ){//只要socket读缓存中还有未读出的数据这段代码就被触发printf( event trigger once\n );memset( buf, \0, BUFFER_SIZE );int ret recv( sockfd, buf, BUFFER_SIZE-1, 0 );if( ret 0 ){close( sockfd );continue;}printf( get %d bytes of content: %s\n, ret, buf );}else{printf( something else happened \n );}} } 2ET边沿触发 Edge Triggered socket的接收缓冲区状态变化时触发读事件即空的接收缓冲区刚接收到数据时触发读事件从无到有socket的发送缓冲区状态变化时触发写事件即满的缓冲区刚空出空间时触发读事件从有到无仅在状态变化时触发事件 ET 处理流程 accept 一个一个连接添加到 epoll 中监听 EPOLLIN|EPOLLOUT 事件当 EPOLLIN 事件到达时读取 fd 中的数据并处理read 需要一直读直到返回 EAGAIN 为止当需要写出数据时把数据 write 到fd中直到数据全部写完或者 write 返回 EAGAIN当 EPOLLOUT 事件到达时继续把数据 write 到fd中直到数据全部写完或者 write 返回 EAGAIN 从 ET 的处理过程中可以看到ET 的要求是需要一直读写直到返回 EAGAIN否则就会遗漏事件。而 LT 的处理过程中直到返回 EAGAIN 不是硬性要求但通常的处理过程都会读写直到返回 EAGAIN但 LT 比 ET 多了一个开关 EPOLLOUT 事件的步骤 当我们使用 ET 模式的 epoll 时我们应该按照以下规则设计 在接收到一个 I/O 事件通知后立即处理该事件。程序在某个时刻应该在相应的文件描述符上尽可能多地执行I/O。在ET模式下在使用epoll_ctl注册文件描述符的事件时应该把描述符设置为非阻塞的非常重要。 因为程序采用循环ET里面采用while循环看清楚呦LE是if判断来对文件描述符执行尽可能多的I/O而文件描述符又被设置为可阻塞的那么最终当没有更多的I/O可执行时I/O系统调用就会阻塞。基于这个原因每个被检查的文件描述符通常应该置为非阻塞模式在得到I/O事件通知后重复执行I/O操作直到相应的系统调用(比如read(),write())以错误码EAGAIN或EWOULDBLOCK的形式失败。 //ET模式的工作流程 void et( epoll_event* events, int number, int epollfd, int listenfd ) {char buf[ BUFFER_SIZE ];for ( int i 0; i number; i ){int sockfd events[i].data.fd;if ( sockfd listenfd ){struct sockaddr_in client_address;socklen_t client_addrlength sizeof( client_address );int connfd accept( listenfd, ( struct sockaddr* )client_address, client_addrlength );addfd( epollfd, connfd, true );}else if ( events[i].events EPOLLIN ){//这段代码不会被重复触发所以我们循环读取数据以确保把socket读缓存中的所有数据读出printf( event trigger once\n );while( 1 ){memset( buf, \0, BUFFER_SIZE );int ret recv( sockfd, buf, BUFFER_SIZE-1, 0 );if( ret 0 ){//对于非阻塞IO下面条件成立表示数据已经全部读取完毕。//此后epoll就能再次触发sockfd上的EPOLLIN事件已驱动下一次读操作if( ( errno EAGAIN ) || ( errno EWOULDBLOCK ) ){printf( read later\n );break;}close( sockfd );break;}else if( ret 0 ){close( sockfd );}else{printf( get %d bytes of content: %s\n, ret, buf );}}}else{printf( something else happened \n );}} } 4、使用 Demo 基于 epoll 实现的高并发 TCP 服务器。 #include stdio.h #include stdlib.h #include string.h #include unistd.h #include arpa/inet.h #include sys/epoll.h #include errno.h #include fcntl.h#define PORT 8080 #define MAX_EVENTS 1024 #define BUFFER_SIZE 1024void set_nonblocking(int sockfd) {int opts;opts fcntl(sockfd, F_GETFL);if (opts 0) {perror(fcntl(F_GETFL));exit(EXIT_FAILURE);}opts (opts | O_NONBLOCK);if (fcntl(sockfd, F_SETFL, opts) 0) {perror(fcntl(F_SETFL));exit(EXIT_FAILURE);} }int main() {int listen_fd, conn_fd, nfds, epoll_fd;struct sockaddr_in server_addr, client_addr;socklen_t client_len sizeof(client_addr);struct epoll_event ev, events[MAX_EVENTS];char buffer[BUFFER_SIZE];int done 0;// 创建监听套接字if ((listen_fd socket(AF_INET, SOCK_STREAM, 0)) -1) {perror(socket);exit(EXIT_FAILURE);}// 设置地址复用int opt 1;if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, opt, sizeof(opt)) -1) {perror(setsockopt);close(listen_fd);exit(EXIT_FAILURE);}// 绑定端口和地址memset(server_addr, 0, sizeof(server_addr));server_addr.sin_family AF_INET;server_addr.sin_addr.s_addr INADDR_ANY;server_addr.sin_port htons(PORT);if (bind(listen_fd, (struct sockaddr *)server_addr, sizeof(server_addr)) -1) {perror(bind);close(listen_fd);exit(EXIT_FAILURE);}// 监听端口if (listen(listen_fd, SOMAXCONN) -1) {perror(listen);close(listen_fd);exit(EXIT_FAILURE);}// 创建 epoll 实例epoll_fd epoll_create1(0);if (epoll_fd -1) {perror(epoll_create1);close(listen_fd);exit(EXIT_FAILURE);}// 将监听套接字添加到 epoll 实例中ev.events EPOLLIN;ev.data.fd listen_fd;if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, ev) -1) {perror(epoll_ctl: listen_fd);close(listen_fd);close(epoll_fd);exit(EXIT_FAILURE);}printf(Server is listening on port %d\n, PORT);// 主循环等待事件并处理while (!done) {nfds epoll_wait(epoll_fd, events, MAX_EVENTS, -1);if (nfds -1) {perror(epoll_wait);close(listen_fd);close(epoll_fd);exit(EXIT_FAILURE);}for (int i 0; i nfds; i) {if (events[i].data.fd listen_fd) {// 处理新的连接请求while ((conn_fd accept(listen_fd, (struct sockaddr *)client_addr, client_len)) ! -1) {set_nonblocking(conn_fd);ev.events EPOLLIN | EPOLLET; // 边缘触发模式ev.data.fd conn_fd;if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_fd, ev) -1) {perror(epoll_ctl: conn_fd);close(conn_fd);continue;}printf(New connection from %s:%d\n, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));}if (errno ! EAGAIN errno ! EWOULDBLOCK) {perror(accept);done 1;}} else {// 处理客户端数据或断开连接int client_fd events[i].data.fd;ssize_t bytes_read read(client_fd, buffer, sizeof(buffer) - 1);if (bytes_read -1) {if (errno ! EAGAIN errno ! EWOULDBLOCK) {perror(read);close(client_fd);epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_fd, NULL); // 从 epoll 监听队列中删除文件描述符}} else if (bytes_read 0) {// 客户端关闭连接printf(Closed connection on descriptor %d\n, client_fd);close(client_fd);epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_fd, NULL); // 从 epoll 监听队列中删除文件描述符} else {// 回显数据给客户端buffer[bytes_read] \0;write(client_fd, buffer, bytes_read);}}}}close(listen_fd);close(epoll_fd);return 0; } 四、参考链接 Linux下的I/O复用技术 — epoll如何使用epoll_create、epoll_ctl、epoll_wait 以及 LT/ET 使用过程解析_主动去触发epoll事件-CSDN博客https://blog.csdn.net/JMW1407/article/details/107963618 还在用多线程试试 Linux select 这个‘神操作’吧https://mp.weixin.qq.com/s/sRXjRUZS1BVx1ZtBsZifug
http://www.dnsts.com.cn/news/82733.html

相关文章:

  • 没有域名的网站网站主体负责人能查询到吗
  • 商业网站开发 流程深圳网页设计与制作工资多少钱
  • 德州极速网站建设源代码查看WordPress文件
  • 宁波网站建设七米网上给别人做网站
  • 做外贸建网站多少钱工信部域名备案管理系统
  • 巴中+网站建设桂林网站设计制作
  • 突泉建设局三务公开网站实惠福步外贸论坛
  • 2019年做网站怎么看一个网站用什么程序做的
  • 企业网站是什么微商怎么做
  • 金华网站建设价格wordpress动态订单
  • 手机网站建设市场塘沽网站开发
  • 南充房产信息网站优化网站建设
  • 网站 分站深圳app定制开发外包公司
  • 网站推广途径及要点网站更改备案信息吗
  • 网站 跳出率 多少沧州营销软件
  • 网站开发需要配置哪些人员增城新塘网站建设
  • 淘宝网站是谁做的好处网站建设开题报告数据库建立
  • 江门网站建设推广策划设计好的装修公司
  • html网站免费模板胶南网站建设
  • 福建省建设厅网站林瑞良北京网页设计公司
  • wordpress读取父分类列表企业网站改版seo
  • 网站建设维护资质做水果蔬菜生意网站
  • c 网站开发社区网站制作
  • 招远网站建设招聘手机网站作用
  • 2003访问网站提示输入用户名密码wordpress 上传附件按钮美化
  • 西安建网站的公司wordpress 清除cookie
  • 建设银行贷款官方网站电子商务网站特色
  • 网站建设模板个人泰州seo
  • 闵行网站设计有限责任公司成立条件
  • 做网站注意的问题做网站找哪家好要钱吗