八股文面试攻略四:网络篇

一、网络编程

1、简述Reactor网络模型编程

Reactor 模式是一种典型的 I/O 多路复用 + 事件驱动 的高性能网络编程模型,被广泛应用于服务器端开发(如 Nginx、Netty、libevent)。它的核心思想是:通过事件分离、回调机制和非阻塞 I/O,提高服务器并发处理能力

核心思想:

  • 事件分发器(Reactor):负责监控多个客户端连接请求与I/O事件(read、write、accept),并在事件发生后分给对应的事件处理器。
  • 事件处理器(handle):每一类事件对应一个处理器。当事件发生时Reactor调用相应的Handle进行业务逻辑处理
  • 非阻塞IO(epoll、select、poll):多个连接复用一个线程进行监听,减少大量阻塞导致的线程开销。

Reactor 网络模型通过 I/O 多路复用 + 事件驱动的方式,让少量线程处理大量网络连接事件,是高性能服务器的经典编程模型。

分为:单Reactor单线程、单Reactor多线程、多Reactor多线程。

2、比较Reactor与Proactor区别

**Reactor是非阻塞同步I/O,**操作系统只负责通知"可以读/写",应用程序负责真正的 I/O 操作。

**Proactor 是异步 I/O 模式。**操作系统负责真正的读写,完成后通知应用程序处理结果。

3、连接断开有哪几种判定方式?

网络通信中,一条 TCP 连接是否"断开",通常可以通过以下几种方式判断

  1. recv()/read()返回0,对方正常关闭(FIN)
  2. recv()/read()返回-1且error表示严重错误(异常断开的)
  3. send()/write() 返回错误(如 EPIPE、SIGPIPE)
  4. epoll/select/kqueue 事件异常(HUP / ERR)
  5. 对端长时间无响应(心跳超时)
  6. TCP keepalive(内核级)检测断开

TCP 连接断开最可靠的判断是:read=0 或出现严重错误 ;对于无数据传输的情况需要"心跳机制 "或"TCP Keepalive"辅助判断。

4、接收客户端连接有几种方式?

  • 单线程+阻塞accept
  • 多线程+阻塞式accept
  • 非阻塞+I/O复用
  • epoll+非阻塞accept
  • 主从Reactor实现高并发
  • 使用事件库接收连接:libev

接收客户端连接的方式有:阻塞 accept、多线程 accept、非阻塞 + select/poll、epoll Reactor 模型、主从 Reactor、异步事件库、IOCP

5、为什么用户态需要设计读写缓冲区?

提高网络 I/O 性能、减少系统调用成本、解决数据边界和协议解析问题,并保证高并发服务器的稳定运行。

可以从以下方面理解:

  • 系统调用开销大,缓冲区能减少系统调用次数(批处理)
  • TCP 是流(Stream),不保证消息边界 → 需要应用层自己"粘包、拆包"
  • 避免数据丢失:内核缓冲区有限,不能无限接收
  • 便于异步网络模型处理
  • 处理半包、粘包、协议解析必须依赖用户态 buffer
  • 异步写必须缓存未发送的数据

二、网络原理

1、水平触发与边缘触发的区别?在边缘触发下,一个Socket已读取200然后不再处理,是不是剩下的300就无法读取了?

1)水平触发:只要FD上仍有""数据可读/可写" ,epoll就持续通知。没有读完,下次epoll_wait()还会继续触发。最安全,最容易使用。

2)边缘触发只有状态从 无 → 有 时触发一次 。若未把 socket 数据读完,后续不会再次通知。性能更高,但需要一次把数据读完。

在边缘触发下,一个 Socket 已读取 200,然后不再处理,是不是剩下的 300 就无法读取了?

是的 ,剩下的 300 你将无法再读出来,除非有新的数据到达触发事件。这是 ET 的典型"事件丢失"情况。数据不会丢失,仍在内核缓冲期,直到下次事件触发才能读取。

2、CLOSE_WAIT和TIME_WAIT是什么?如何排查?有什么意义?

首先我们清楚这两个状态属于TCP'四次挥手。

产生原因:

  • CLOSE_WAIT :收到对端FIN包并回复ACK后,等待应用层调用close()发送FIN的阶段
  • TIME_WAIT:发送最后一个ACK后,保持2MSL(通常60s)防止报文混淆

CLOSE_WAIT 状态表示:

对端主动关闭连接(发送 FIN)→ 本端已经收到 FIN,但本端还没有 close()。

**常见原因:**应用未正确关闭socket;文件描述符泄漏;线程阻塞导致无法处理关闭事件

CLOSE_WAIT 多=代码问题。可以通过以下解决:Valgrind检测内存泄漏

TIME_WAIT 状态表示:

主动关闭连接的一方,在发送 FIN + 收到 ACK 后,需要等待 2MSL 时间。

TIME_WAIT 多=业务模式问题,

服务端:短连接多,可以采用长连接,并使用epoll+连接池减少重复建立连接。

意义

  • CLOSE_WAIT:标识连接半关闭状态,等待应用层处理收尾工作
  • TIME_WAIT:保证可靠终止、防止旧报文干扰新连接

3、TCP三次握手的过程?为什么可以是二次握手?

tcp建立过程:

  • 为了实现可靠数据传输, TCP 协议的通信双方, 都必须维护一个序列号, 以标识发送出去的数据包中, 哪些是已经被对方收到的。 三次握手的过程即是通信双方相互告知序列号起始值, 并确认对方已经收到了序列号起始值的必经步骤
  • 如果只是两次握手, 至多只有连接发起方的起始序列号能被确认, 另一方选择的序列号则得不到确认

4、TCP四次挥手过程?TIME_WAIT为什么至少设置两倍的MSL时间?

TCP四次挥手:

MSL:指一个 TCP 报文在网络中可能存在的最长时间

本质作用是

  • 保证对端收到最后 ACK
  • 让旧连接的残留报文全部在网络中消失,不影响新连接

场景1:对端没收到 ACK → 重发 FIN(等待 1×MSL)

场景 2:网络上可能还有旧连接的残留报文(再等 1×MSL)

一个用于等待重传,一个用于清除残留数据。

5、什么是连接的半打开,半关闭状态?

半关闭状态:TCP 连接的一端关闭了自己的"发送方向",但仍然保持"接收方向"畅通。

半关闭的用途

  • 应用层协议中表示"发送结束,但要继续接收"
  • 防止死锁(双方都在等对方写)
  • HTTP/1.0 的请求后关闭写端,等待响应

半打开状态:连接的一端已经异常退出(崩溃/网络断开/无正常 FIN),而另一端仍然认为连接是"打开"的。

半打开的危害

  • 服务端可能为"死连接"保留资源
  • 心跳机制检测不到会造成长时间僵尸连接
  • 高并发下会导致资源耗尽

检测方法

  • 心跳检测、keepalive
  • 超时机制

半关闭:一端发送通道关闭,但接收通道仍开,是 TCP 协议正常行为。

半打开:连接一端失效而另一端不知情,是异常状态,需要心跳/超时清理。

6、Linux IO模型有哪几种?简述IO多路复用机制?

  1. 阻塞I/O:read() / recv() 会一直阻塞,直到数据到达并复制到用户缓冲区完成。
  2. 非阻塞 I/O :read() / recv() 不阻塞,若无数据立即返回 EAGAIN
  3. I/O 多路复用:使用一个线程监控多个 fd,当某个 fd 准备好(可读/可写)时再进行 I/O。
  4. 信号驱动 I/O:内核收到数据时,向进程发送 SIGIO 信号,通知它可以读。
  5. 异步 I/O :用户发起 aio_read,内核异步执行读写并自动填写用户缓冲区,完成后发送通知。

IO 多路复用是什么?

通过一个线程监听多个 socket,当任意 socket 就绪时再执行 read/write,从而避免为每个连接建立一个线程

常用实现:epoll、poll、select。

7、阻塞IO与非阻塞IO区别?

阻塞I/O:当应用调用read()/recv()时,如果数据没有到达线程就一直阻塞等待。直到数据准备好并复制到用户缓冲区后才返回。整个等待期间,线程被挂起,无法做其他事。

非阻塞 I/O:当应用调用read()/recv()时,若数据没有准备好立即返回-1并设置errno=EAGAIN,不会阻塞数据到达。函数不会阻塞立即返回,通过询问查询。

8、select、poll、epoll三者区别

|----------|----------|--------------|-------------------|
| | select | poll | epoll |
| 底层结构 | 位图 | 数组 | 红黑树+就绪列表 |
| 最大连接数 | 1024 | 无上限 | 无上限 |
| 事件检查方式 | 轮询扫描O(n) | 轮询扫描O(n) | 回调通知 O(1) |
| 内核、用户态拷贝 | 每次拷贝fd集合 | 每次拷贝整个 fd 列表 | 只在注册时拷贝一次 |
| 触发方式 | 水平触发 | 水平触发 | (LT)水平触发+(ET)边缘触发 |
| 性能 | 差 | 较差 | 高性能 |
| 使用难度 | 简单 | 简单 | 较复杂(边缘触发)ET难度大 |

9、为什么边缘触发一定是非阻塞IO?

ET 模式只在状态变化的瞬间触发事件,如果套接字是阻塞式的,那么程序在事件处理过程中可能因阻塞而错过读取全部数据,从而永远不会再次获得读事件 → 造成死锁或假死。

因此 ET 必须使用非阻塞 I/O,让程序能读到 EAGAIN 才能保证状态正确推进。

10、描述IO多路复用机制

把等待多个 I/O 事件的工作交给内核,让一个线程高效管理大量连接,从而避免为每个连接创建一个线程或进程。

IO 多路复用是让一个线程同时监控多个 socket,只在 socket 就绪时进行 I/O,

避免线程阻塞等待,是高并发服务器的基础。

select 和 poll 需要遍历所有 fd,效率 O(n);

epoll 使用事件通知机制,只处理就绪的 fd,效率 O(1),适合大量并发连接。

相关推荐
q***76662 小时前
显卡(Graphics Processing Unit,GPU)架构详细解读
大数据·网络·架构
王中阳Go2 小时前
面试被挂的第3次,面试官说:你懂的LLM框架,只够骗骗自己
面试·职场和发展
网安小白的进阶之路2 小时前
B模块 安全通信网络 第一门课 园区网实现与安全-1
网络·安全
terminal0073 小时前
浅谈useRef的使用和渲染机制
前端·react.js·面试
川西胖墩墩4 小时前
流程图在算法设计中的实战应用
数据库·论文阅读·人工智能·职场和发展·流程图
dragoooon344 小时前
[Linux网络——Lesson2.socket套接字 && 简易UDP网络程序]
linux·网络·udp
独行soc4 小时前
2025年渗透测试面试题总结-254(题目+回答)
网络·python·安全·web安全·adb·渗透测试·安全狮
i_am_a_div_日积月累_5 小时前
websocket设置和断开机制
网络·websocket·网络协议
Eric_Makabaka5 小时前
计算机网络重要知识点
java·网络·计算机网络