select 高并发下性能差因每次调用需全量拷贝 fd 集合并线性遍历,o(n) 复杂度致千连接即延迟上升;应改用 epoll_wait,o(1) 返回就绪 fd,且无 1024 限制。

为什么 select 在高并发下会拖慢 TCP 服务?
因为 select 每次调用都要把整个 fd 集合从用户态拷贝到内核态,且内核要线性遍历所有 fd 判断就绪状态——连接数一过千,延迟就明显上升。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- Linux 上优先用
epoll_wait,它只返回就绪的 fd,不遍历全集,时间复杂度从 O(n) 降到 O(1) 平均情况 - 避免在循环里反复调用
FD_SET构造 fd_set;epoll用epoll_ctl增删一次就够了 -
select最大支持 fd 数通常为 1024(受FD_SETSIZE限制),而epoll只受限于系统内存和rlimit
如何避免 recv 阻塞或读不全导致粘包?
TCP 是字节流协议,recv 不保证一次返回一个完整业务包;直接按固定长度读,遇到网络抖动或小包合并就会出错。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 不要依赖单次
recv读完全部数据;用循环 + 缓冲区拼接,直到收到预期长度或遇到分隔符 - 应用层加协议头,比如前 4 字节存 payload 长度(用
ntohl/htonl处理字节序),再按需读取 - 设置 socket 为非阻塞模式(
fcntl(fd, F_SETFL, O_NONBLOCK)),配合epoll使用,避免单连接卡住整个事件循环
send 返回值小于请求长度时,为什么不能直接丢弃剩余数据?
这是 TCP 发送缓冲区满的正常信号,不是错误。Linux 内核只把能塞进缓冲区的数据拷走,返回实际字节数;剩余部分必须由你手动缓存、重试,否则消息就丢了。
PHP是一种功能强大的网络程序设计语言,而且易学易用,移植性和可扩展性也都非常优秀,本书将为读者详细介绍PHP编程。 全书分为预备篇、开始篇和加速篇三大部分,共9章。预备篇主要介绍一些学习PHP语言的预备知识以及PHP运行平台的架设;开始篇则较为详细地向读者介绍PKP语言的基本语法和常用函数,以及用PHP如何对MySQL数据库进行操作;加速篇则通过对典型实例的介绍来使读者全面掌握PHP。 本书
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 检查
send返回值:等于 0 表示对端关闭;小于请求长度说明要重发;-1 且errno == EAGAIN或EWOULDBLOCK表示缓冲区满,需等待可写事件 - 为每个连接维护发送队列(如
std::deque<:vector>></:vector>),在epoll收到EPOLLOUT后继续发送 - 避免在单次
send前把大 buffer 全部 memcpy 到临时空间——高频小包场景下,内存拷贝开销会吃掉性能优势
为什么多线程直接共享 socket fd 容易出问题?
socket fd 本身是进程级资源,多个线程调用 send/recv 不会崩溃,但竞争条件会让逻辑混乱:比如两个线程同时 recv,谁读到哪段数据完全不可控。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 一个 socket fd 只由一个线程负责 I/O;用
epoll+ 单 Reactor 模型最稳妥 - 需要并行处理业务逻辑?把解包后的任务投递到线程池,但收发和协议解析仍在 I/O 线程完成
- 真要多线程收发(如某些 UDP 场景),得用
SO_REUSEPORT让内核分流连接,每个线程 bind 自己的 socket,而不是共享同一个 fd
真正卡住性能的往往不是底层 syscall,而是缓冲区管理方式、协议解析是否零拷贝、以及事件驱动逻辑有没有隐式阻塞。这些细节没对齐,换再快的 I/O 多路复用也没用。










