尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
阻塞和非阻塞
“阻塞”和“非阻塞”主要是在说当一个操作暂时无法完成时调用者是停在那里等还是立刻返回。它经常出现在read() write() recv() send() accept() connect()尤其是网络编程里。一、阻塞是什么意思阻塞就是函数暂时做不了就一直等直到能做为止。比如char buf[1024]; recv(sockfd, buf, sizeof(buf), 0);如果sockfd是阻塞 socket并且现在没有数据可读那么recv会卡住。程序会停在这里recv(sockfd, buf, sizeof(buf), 0); // 没数据就不往下走直到发生下面某种情况对端发来了数据对端关闭连接出错被信号打断设置了超时并超时所以阻塞 I/O 的感觉是你要读数据 现在没数据。 那你在这里等着。二、 非阻塞是什么意思非阻塞就是函数暂时做不了也不会等而是马上返回。同样是recv(sockfd, buf, sizeof(buf), 0);如果sockfd是非阻塞 socket并且现在没有数据可读那么recv不会卡住而是返回-1并设置errno EAGAIN || errno EWOULDBLOCK意思是现在没数据你之后再试。所以非阻塞 I/O 的感觉是你要读数据 现在没数据。 我不等直接告诉你暂时不能读。三、 一个直观比喻阻塞你去奶茶店买奶茶。 店员说还没做好。 你站在柜台前一直等什么都不干。 等做好后拿到奶茶再走。非阻塞你去奶茶店问好了没。 店员说还没好。 你马上离开去做别的事。 过一会儿再来问。在程序里“站在那里等”就是线程被阻塞。四、阻塞 read/recv 示例char buf[1024]; ssize_t n recv(fd, buf, sizeof(buf), 0); std::cout recv returned\n;如果fd是阻塞的并且没有数据那么std::cout recv returned\n;这一行不会执行直到recv返回。也就是说整个线程卡在recv上。五、 非阻塞 recv 示例先把 socket 设置成非阻塞#include fcntl.h #include unistd.h void set_nonblocking(int fd) { int flags fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, flags | O_NONBLOCK); }然后char buf[1024]; ssize_t n recv(fd, buf, sizeof(buf), 0); if (n 0) { // 读到了 n 字节 } else if (n 0) { // 对端关闭连接 } else { if (errno EAGAIN || errno EWOULDBLOCK) { // 现在没数据不是错误之后再读 } else { // 真正的错误 } }非阻塞 I/O 的核心就是要处理EAGAIN EWOULDBLOCK它们表示“现在做不了但不是致命错误”。六、accept 的阻塞和非阻塞accept用来接受新连接。阻塞模式下int client_fd accept(listen_fd, nullptr, nullptr);如果没有客户端连接进来程序就卡住。非阻塞模式下int client_fd accept(listen_fd, nullptr, nullptr);如果暂时没有新连接返回-1并且errno EAGAIN || errno EWOULDBLOCK所以非阻塞服务器里经常这样写while (true) { int client_fd accept(listen_fd, nullptr, nullptr); if (client_fd 0) { set_nonblocking(client_fd); // 加入 epoll } else { if (errno EAGAIN || errno EWOULDBLOCK) { break; } // 其他错误 break; } }这里的while很常见尤其是epoll的边缘触发模式下。七、 send/write 的阻塞和非阻塞很多人以为send一定能立刻把数据发出去其实不是。阻塞 socketsend(fd, data, len, 0);如果内核发送缓冲区满了send会卡住直到缓冲区有空间。非阻塞 socketssize_t n send(fd, data, len, 0);如果发送缓冲区满了它返回-1并设置errno EAGAIN || errno EWOULDBLOCK还有一个重要点send可能只发送一部分。比如你想发 10000 字节ssize_t n send(fd, data, 10000, 0);返回值可能是3000意思是只发出去了 3000 字节剩下 7000 字节要你之后继续发。所以非阻塞网络程序里一般要有发送缓冲区struct Connection { int fd; std::string write_buffer; };没发完的数据放进write_buffer等下次EPOLLOUT再发。八、connect 的阻塞和非阻塞connect也有区别。阻塞模式connect(fd, addr, len);会一直等到连接成功或失败。非阻塞模式下int ret connect(fd, addr, len);如果连接还在进行中通常返回-1并且errno EINPROGRESS意思是连接正在建立还没完成。这时你可以用epoll监听这个 fd 的可写事件EPOLLOUT等它可写时再用getsockopt检查是否连接成功。九、阻塞的问题阻塞本身不是坏事。简单程序里阻塞很好用while (true) { int client accept(listen_fd, nullptr, nullptr); recv(client, buf, sizeof(buf), 0); send(client, response, response_len, 0); }代码直观。但高并发服务器里阻塞会带来问题。假设一个线程处理一个连接10000 个连接 10000 个线程这会有线程创建成本高上下文切换成本高栈内存占用大调度开销大慢连接会占住线程如果一个客户端一直不发数据线程就一直卡在recv上。十、非阻塞的问题非阻塞也不是万能的。如果你这么写while (true) { ssize_t n recv(fd, buf, sizeof(buf), 0); if (n 0 errno EAGAIN) { continue; } }这会疯狂空转CPU 直接飙高。因为没有数据时你一直问有数据吗 没有。 有数据吗 没有。 有数据吗 没有。所以非阻塞 I/O 通常要配合select / poll / epoll也就是没有事件时睡眠 有事件时再尝试读写。这就是epoll的作用。十一、 epoll 和非阻塞的关系epoll通常配合非阻塞 fd 使用。典型流程把 socket 设为非阻塞 注册到 epoll epoll_wait 等事件 有 EPOLLIN 再 recv 读到 EAGAIN 停止 继续 epoll_wait代码结构像这样while (true) { int n epoll_wait(epfd, events, 1024, -1); for (int i 0; i n; i) { int fd events[i].data.fd; if (events[i].events EPOLLIN) { while (true) { ssize_t ret recv(fd, buf, sizeof(buf), 0); if (ret 0) { // 处理数据 } else if (ret 0) { // 对端关闭 close(fd); break; } else { if (errno EAGAIN || errno EWOULDBLOCK) { break; } close(fd); break; } } } } }这就是高性能网络编程里最经典的模式之一。
RELATED

相关推荐

惠普暗影精灵笔记本终极性能控制工具:OmenSuperHub完整使用指南

惠普暗影精灵笔记本终极性能控制工具:OmenSuperHub完整使用指南

惠普暗影精灵笔记本终极性能控制工具:OmenSuperHub完整使用指南 【免费下载链接】OmenSuperHub Control Omen laptop performance, fan speeds, and keyboard lighting, and unlock power limits. 项目地址: https://gitcode.com/gh_mirrors/om/OmenSuperHub …

📅 2026/10/6 1:18:49
抄底摸顶神器 同花顺期货通指标

抄底摸顶神器 同花顺期货通指标

今天给大家带来是一款同花顺期货通指标,并且已经上架到同花顺期货通的指标广场上了。喜欢的朋友可以去指标广场安装试用!!友情提示:(指标只是辅助,不作建议)拼多多店铺:指标公式编写…

📅 2026/10/7 3:56:00
实时公交到站接口排错手记:从 401 到 429 的完整排查路径

实时公交到站接口排错手记:从 401 到 429 的完整排查路径

适用场景与接口能力边界 实时公交到站接口用于输入城市和站名,查询该站各公交线路的到站信息,包括车牌、预计到站时间、剩余站数、票价和终点方向。典型使用场景有出行助手的数据源、公交小程序、生活服务集成、电子站牌类展示页等。 接入前先明确接口能…

📅 2026/9/9 4:49:14
MORE NEWS

更多资讯

📰

停车场管理系统课程设计:用栈、队列、链表一题通关

简介:面向数据结构课程设计的学生,这是一份以C语言实现的停车场管理系统完整资料,源码基于链栈数据结构完成,覆盖车辆入场、出场、增删查改等核心逻辑,可自动计算停留时长与应收费用,命令行终端界面经过优化…

📰

仿微信聊天系统源码解析:WinForm桌面端IM开发实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

阿里 Qoder 智能体工作台实战:用 TaoToken 统一 Key 打通 Harness 与 Agent 工作流

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

OpenClaw Talk 模式实战:TaoToken 统一 Key 打通 Voice Call 语音链路配置指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

工控数据类型与值范围详解:从PLC到Modbus的解析避坑指南

1. 从一次通讯调试翻车说起:为什么数据类型值得单独拎出来讲刚入行那会儿,我接手过一个改造项目:用上位机通过 Modbus RTU 读取一台老设备的温度值。协议文档上白纸黑字写着"温度寄存器地址 40001,单位 0.1℃"。我照着地…

📰

新电脑装机指南:从系统初始化到效率工具的科学配置思路

1. 装机思路的底层逻辑:为什么“必装清单”不能照抄每次帮朋友处理新电脑,我最怕听到的一句话就是“你直接把你的软件清单发我一份”。不是不愿意给,而是这份清单如果直接照搬,大概率会出问题。原因很简单:每个人的硬件…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬