news 2026/5/9 2:10:34

Linux 下网络编程:高性能 IO 多路复用,epoll 事件处理循环

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux 下网络编程:高性能 IO 多路复用,epoll 事件处理循环
for(inti=0;i<nfds;++i){if(events[i].data.fd==listen_sockfd){// --- 处理新连接 ---structsockaddr_inclient_addr{};socklen_t client_len=sizeof(client_addr);intconn_sockfd=accept(listen_sockfd,(structsockaddr*)&client_addr,&client_len);if(conn_sockfd<0){perror("accept failed");continue;}std::cout<<"New connection from "<<inet_ntoa(client_addr.sin_addr)<<":"<<ntohs(client_addr.sin_port)<<std::endl;set_non_blocking(conn_sockfd);// 新连接的套接字也必须是非阻塞的// 将新连接的套接字加入epoll监控event.events=EPOLLIN|EPOLLET;// 使用边缘触发(ET)模式event.data.fd=conn_sockfd;if(epoll_ctl(epoll_fd,EPOLL_CTL_ADD,conn_sockfd,&event)<0){perror("epoll_ctl add client failed");close(conn_sockfd);}}else{// --- 处理客户端数据 ---intconn_sockfd=events[i].data.fd;charbuffer[1024];ssize_t count=read(conn_sockfd,buffer,sizeof(buffer));if(count<0){// 非阻塞模式下,EAGAIN或EWOULDBLOCK表示数据已读完if(errno==EAGAIN||errno==EWOULDBLOCK){// std::cerr << "Read finished for fd " << conn_sockfd << std::endl;}else{perror("read failed");}}elseif(count==0){// 客户端关闭连接std::cout<<"Client "<<conn_sockfd<<" disconnected."<<std::endl;epoll_ctl(epoll_fd,EPOLL_CTL_DEL,conn_sockfd,nullptr);close(conn_sockfd);}else{// 收到数据,回显给客户端// 注意:在实际高性能场景中,write也可能需要处理EAGAINif(write(conn_sockfd,buffer,count)<0){perror("write failed");}}}}

epoll 事件处理循环

这是整个 epoll 服务器的「业务核心」!前面所有代码(socket、bind、listen、epoll_create)都是为了这一段代码服务。

整段代码的核心作用(大白话)

遍历所有触发事件的 socket: 如果是【监听套接字】有动静 → 有新客户上门,接待连接 如果是【普通客户端】有动静 → 客户发消息了,读取并回复

这就是高并发服务器处理成千上万客户端的逻辑!


第一部分:遍历事件

for(inti=0;i<nfds;++i){
  • nfdsepoll_wait返回的就绪事件数量
  • 作用:把这次所有触发了事件的 socket挨个处理一遍

第二部分:判断事件类型(核心分支)

if(events[i].data.fd==listen_sockfd){

原理:

我们之前把监听套接字加入了 epoll 监控。
当它触发EPOLLIN事件 =内核队列里有新连接等待被接收

所以进入这个if一定是来新客户端了!


分支1:处理新客户端连接

1. 接收连接(accept)

intconn_sockfd=accept(listen_sockfd,...);
  • 作用:从内核队列里取出一个连接,创建一个通信套接字
  • listen_sockfd:只负责接客
  • conn_sockfd:专门用来和这个客户端收发数据(一对一专属)

2. 打印客户端信息

inet_ntoa(client_addr.sin_addr)// IP转字符串ntohs(client_addr.sin_port)// 端口转主机序
  • 打印:客户端从哪个IP、哪个端口连过来的

3. 设置非阻塞(超级重要)

set_non_blocking(conn_sockfd);
  • 只要用 epoll(ET模式),所有套接字必须是非阻塞!
  • 保证read/write不会卡住服务器

4. 把新客户端加入 epoll 监控

event.events=EPOLLIN|EPOLLET;// 监控可读 + 边缘触发event.data.fd=conn_sockfd;// 带上通信套接字epoll_ctl(ADD...);// 加入监控列表

重点:EPOLLET 边缘触发

  • ET 模式:epoll最高性能模式
  • 只在数据从无到有时通知一次
  • 效率极高,是高并发服务器标配

至此:

新客户端连接成功!epoll 开始监控它的消息。


分支2:处理客户端数据(else)

进入 else =不是新连接,是已连接客户端发数据来了!

1. 获取客户端套接字

intconn_sockfd=events[i].data.fd;
  • 拿到和这个客户端通信的专属套接字

2. 读取数据

ssize_tcount=read(conn_sockfd,buffer,sizeof(buffer));
  • 从客户端读取消息到缓冲区
  • 返回值count:读到的字节数

3. 三种返回值处理

① count < 0:读取出错

if(errno==EAGAIN){// 正常!非阻塞ET模式下,数据读完了}else{// 真出错了}

② count == 0:客户端断开连接

// 从epoll移除 + 关闭套接字epoll_ctl(DEL...);close(conn_sockfd);
  • 客户端关闭了连接,服务器清理资源

③ count > 0:读到有效数据

write(conn_sockfd,buffer,count);
  • 回显服务器:收到什么,原样发回给客户端

完整流程示意图(秒懂)

  1. 客户端发起连接
  2. 监听套接字触发 EPOLLIN
  3. accept 接收,得到 conn_sockfd
  4. 将 conn_sockfd 加入 epoll
  5. 客户端发消息
  6. conn_sockfd 触发 EPOLLIN
  7. read 读取,write 回写
  8. 客户端断开 → close 清理

极简总结(封神级)

  1. if (listen_sockfd):处理新连接,accept + 加入epoll
  2. else:处理客户端消息,read + write
  3. EPOLLET:高性能边缘触发
  4. 非阻塞:所有socket必须非阻塞
  5. 核心:一个线程,处理成千上万客户端,不卡顿!

整个服务器代码完整闭环

你现在已经看完了Linux高并发epoll服务器全部代码

  1. socket → 创建套接字
  2. setsockopt → 端口复用
  3. bind → 绑定IP端口
  4. listen → 开始监听
  5. epoll_create1 → 创建监控
  6. epoll_ctl(ADD) → 加入监听socket
  7. epoll_wait → 等待事件
  8. for循环处理→ 你刚学的核心逻辑

这就是 Nginx、Redis、SRS 等高并发中间件的底层原理!


最终一句话总结

这段代码,让单线程服务器,能同时处理几万客户端连接和消息,这就是 epoll 的威力!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/9 2:07:56

三星全线退出中国家电市场:真被国货打跑?还是战略大转移?

一、三星真的被国货「打跑」了&#xff1f;近期三星官宣全线停止在中国大陆销售电视、冰箱、洗衣机等全品类家电&#xff0c;消息一出立刻引发热议。不少人高呼「解气」&#xff0c;认为这是国产家电崛起的标志性事件 —— 外资巨头终于被中国品牌打跑了。但事实真的是「兵败撤…

作者头像 李华
网站建设 2026/5/9 2:07:55

从iPhone备份提取Apple Watch健康数据的开源工具WatchClaw详解

1. 项目概述&#xff1a;一个能“抓取”Apple Watch数据的开源利器如果你是一名iOS或watchOS开发者&#xff0c;或者对可穿戴设备的数据分析感兴趣&#xff0c;那你很可能遇到过这样的困境&#xff1a;想深入研究Apple Watch采集到的那些丰富数据——心率、步数、活动能量、睡眠…

作者头像 李华
网站建设 2026/5/9 1:55:07

ARM scatter文件详解:内存布局控制与工程实践

1. ARM scatter文件基础概念与语法结构在嵌入式系统开发中&#xff0c;内存布局的控制是确保系统稳定运行的关键环节。ARM scatter文件&#xff08;分散加载描述文件&#xff09;作为链接器脚本的一种实现&#xff0c;其核心作用在于精确控制代码和数据在内存中的物理分布。与传…

作者头像 李华
网站建设 2026/5/9 1:53:45

MoE-LLM性能瓶颈分析与优化实践

1. MoE-LLM性能瓶颈的本质特征现代大型语言模型(LLM)的推理过程本质上是在内存带宽和计算资源之间寻找平衡的艺术。通过对OLMo-2系列模型(1B/7B/13B/32B)的剖面分析&#xff0c;我们发现了一个关键现象&#xff1a;在标准解码器层中&#xff0c;Attention模块消耗了68-72%的推理…

作者头像 李华
网站建设 2026/5/9 1:52:38

LangGraph 核心概念全解笔记

一、LangGraph 是什么&#xff1f;LangGraph 是 LangChain 团队官方推出的基于状态机的智能体 (Agent) 开发框架&#xff0c;它基于 LangChain 生态构建&#xff0c;专门解决了 LangChain 原生 LCEL 链式调用的核心短板&#xff0c;用来构建有状态、可循环、可持久化、支持复杂…

作者头像 李华
网站建设 2026/5/9 1:51:16

ngx_handle_read_event

1 定义 ngx_handle_read_event 函数 定义在 ./nginx-1.24.0/src/event/ngx_event.cngx_int_t ngx_handle_read_event(ngx_event_t *rev, ngx_uint_t flags) {if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {/* kqueue, epoll */if (!rev->active && !rev->rea…

作者头像 李华