第 1 章
计算机网络
1. 建立 TCP 连接为什么要三次握手而不是两次?
TCP 三次握手包括①客户端发送 SYN,②服务端回复 SYN-ACK,③客户端再发送 ACK。
这里需要第三次握手是为了防止服务器开出一个客户端不需要的连接。例如客户端发送 SYN 超时,建立连接失败,但过了一会这个 SYN 到达了服务端。此时如果只有两次握手,服务端直接开通连接,就白白浪费了服务器资源,服务器会单向监听一个永远收不到数据的端口。所以服务端必须向客户端再确认一次,才能开通一个完整的、全双工的 TCP 连接。
2. 断开 TCP 连接为什么要四次挥手而不是三次?
TCP 四次挥手包括①客户端发送 FIN,②服务端回复 ACK,③服务端发送 FIN,④客户端回复 ACK。
服务端收到 FIN 时,自己的数据可能还没发送完,所以不会马上回复 FIN,而是先发一个 ACK,等数据发完了再主动向客户端发 FIN——这就是 ACK 和 FIN 要分两次发的原因。如果服务端迟迟不发 ACK 或 FIN,客户端会自己超时断开连接;如果服务端发了 FIN 但没收到客户端的 ACK,服务端也会自己超时断开连接。客户端回复完 ACK 后还要等 2MSL,确保服务端确实收到了这个 ACK,防止服务端因为没收到确认、重传 FIN 时没人处理。
断连比建连多一次,是因为 TCP 是全双工的,客户端到服务端、服务端到客户端是两个独立方向,需要分别关闭、分别确认;就算某个方向的确认丢了,超时机制也能兜底。
3. 什么时候用 TCP/UDP?视频通话为什么不用 TCP?
需要确保可靠性的时候用 TCP,需要确保低延迟的时候用 UDP。视频通话可以容忍部分丢包,但不能容忍延迟,所以用 UDP;如果视频通话用 TCP,丢包后 TCP 会重传旧帧,反而会造成更大的延迟。
| TCP | UDP | |
|---|---|---|
| 连接 | 面向连接(全双工),一对一,需要维护连接状态 | 无连接(不存在连接状态/概念),更轻量,可以一对多 |
| 头部开销 | 20 字节 | 8 字节 |
| 流量/拥塞控制 | 有 | 没有 |
流量控制和拥塞控制都是传输层机制:流量控制靠接收方在头部 Window 字段里告知接收窗口,拥塞控制靠发送方自己维护拥塞窗口——拥塞窗口不在包头里,是发送方本地状态。
4. TCP 是怎么保证可靠传输?超时重传和快重传有什么区别?
可靠传输指的是 TCP 数据包顺序一致,每个包不出错、不丢失、不重复。TCP 主要通过编号确认包顺序,通过校验确保数据正确,通过重传确保不丢失。
TCP 重传分为超时重传和快重传。在触发条件上,超时重传需要等待计时器超时,而快重传在连续收到 3 个相同的 ACK 就进行重传。在响应时间上,超时计时器时间是通过网络质量动态计算,通常明显大于快重传大约 RTT + 发 3 个包的时间。
举个快重传的例子:发送方连续发出数据包 1、2、3、4、5,接收方收到了 1、3、4、5,唯独没收到 2。这时接收方每收到一个乱序到达的包,都会回一个 ACK=2,表示"我还在等 2"。因为发数据包的速度很快,发送方很快就会连续收到 3 个相同的 ACK=2,判断出 2 大概率丢了,于是直接重传。
这里要连续收到 3 个重复 ACK 才触发(而不是 1 个),是为了过滤降噪,避免重传太频繁,因为网络抖动也有可能造成轻微乱序。有了快重传还保留超时重传,是为了作为最终兜底。
5. 滑动窗口解决什么问题?它和拥塞窗口是什么关系?
滑动窗口解决的是停等协议效率太低的问题。在停等协议中,发送方每次只能发一个,必须等接收方回复才能发下一个,效率很低。但如果发送方一直持续发送,接收方处理不过来,也会出现缓冲区溢出的问题。滑动窗口就是用来解决这两个问题的:发送方可以批量发送窗口内的数据,但要等收到回复、窗口向前滑动之后才能继续发送新的数据包。接收窗口由接收方通告,这样既提升了发送效率,又保护了接收方的缓冲区。
发送方的合理速率不仅取决于接收方接收能力,还取决于网络拥塞程度,因此实际发送窗口 = min(接收窗口, 拥塞窗口)。其中,拥塞窗口是用来保护网络的,防止发送速率过快压垮共享链路,由发送方自己估算;而接收窗口保护的是接收方缓冲区,大小由接收方来控制。
6. 拥塞控制算法有哪四种机制?慢启动和拥塞避免的区别是什么?
拥塞控制包括慢启动、拥塞避免、快重传、快恢复四个机制。
慢启动是指连接刚建立时,发送方还不知道网络具体的拥塞情况,于是先把拥塞窗口置为 1,每收到一轮 ACK 就翻倍,指数增长,快速逼近网络容量。窗口涨过慢启动阈值(ssthresh)后,转入第二个阶段——拥塞避免。此时拥塞窗口线性增长,每轮 ACK 只加 1,在接近网络容量时谨慎试探。
第三个阶段是网络真的发生了拥塞,有两种情况:
| 严重拥塞(超时重传) | 轻微拥塞(快重传,收到 3 个重复 ACK) | |
|---|---|---|
| 说明 | 连 ACK 都收不到,网络拥塞很严重 | ACK 还能持续到达,网络只是有波动 |
| ssthresh | 砍到当前拥塞窗口的一半 | 同样砍到一半 |
| 拥塞窗口 | 直接重置为 1,重新慢启动+拥塞避免 | 从砍半后的值开始线性增长,不清零——这就是快恢复 |
慢启动和拥塞避免的区别在于增长方式:慢启动从 1 开始指数增长,涨到 ssthresh 为止,目的是快速逼近网络容量;拥塞避免从 ssthresh 开始线性增长,目的是在接近网络容量时谨慎增加。
这四个机制合起来,就是 TCP Reno 版本的拥塞控制逻辑。
7. TCP 有粘包问题吗?怎么解决?
TCP 有粘包/拆包问题,这是由 TCP 字节流协议只保证按序到达、不保留消息边界的特点导致的。如果应用层调用两次 send() 发送消息,TCP 可能把数据合并成一个段发出去(Nagle 算法愿意攒小包);对端 recv() 从缓冲区里捞数据时,捞出的是"当前缓冲区中的所有字节",可能一次捞到两条消息粘在一起(粘包),也可能只捞到半条消息(拆包)。总之,应用层看到的是没有消息边界的字节流,TCP 段的边界只给传输层自己看。相比之下,UDP 就没有这个问题,每次 sendto() 发送和 recvfrom() 接收的都是有边界的独立数据报。
TCP 没有消息边界的问题需要在应用层解决,由应用层自己定义消息边界。常用的方法有:①固定长度,每条消息定死 N 字节,长度不够就零填充,简单但浪费空间;②使用分隔符,例如 HTTP header 用 \r\n 标记消息结束,缺点是消息体不能出现这个分隔符;③使用长度前缀,消息开头写明正文长度,接收方按长度读取正文。
8. GET 和 POST 有什么区别?讲一下常见的 HTTP 状态码?
| GET | POST | |
|---|---|---|
| 参数位置 | URL 里显式带参数 | 放在请求体里,URL 上看不到 |
| 长度限制 | 有,不适合传大数据 | 无限制,适合表单、文件上传等大量数据 |
| 幂等性 | 幂等——多次请求,结果不变 | 不幂等——每次提交都是一次修改,多次操作影响不同 |
| 语义 | 向服务器请求信息 | 向服务器提交修改 |
常见 HTTP 状态码按大类分:200 成功,300 重定向,400 客户端错误,500 服务器端错误。具体几个高频的:
| 状态码 | 含义 |
|---|---|
| 301 Moved Permanently | 永久重定向,资源已经永久搬到新地址 |
| 302 Found | 临时重定向,比如网站维护中临时转到别处 |
| 403 Forbidden | 有这个资源,但没有访问权限 |
| 404 Not Found | 根本没有这个资源 |
9. HTTPS 和 HTTP 有什么区别?
HTTP 端口 80,HTTPS 端口 443。HTTPS 在 HTTP 和 TCP 之间加了一层 TLS(以前叫 SSL),对数据加密、对网站做身份认证,并确保数据完整性。
TLS 握手发生在 TCP 三次握手之后、HTTP 请求之前,包括①客户端发送 Client Hello,告知自己支持的加密算法;②服务端回复 Server Hello,选定加密算法并传回自己的数字证书(含公钥);③客户端验证证书链,生成一个随机密钥,用服务端公钥加密后发送过去;④服务端收到这个随机密钥,双方切换到对称加密进行后续通信。
切换到对称加密的原因是:非对称加密安全但慢,对称加密快但密钥交换不安全——TLS 用非对称加密来交换对称密钥,兼顾了安全和性能。
10. 讲一下 HTTP 不同版本的区别?
早期 HTTP/1.0 是一个资源请求完就关连接,拿多个资源要开多次 TCP 连接。HTTP/1.1 改成持久连接(keep-alive),一个 TCP 连接发完一个资源请求不会马上关、可以复用,但同一条连接上还是不能并发处理多个 HTTP 请求——于是有了 HTTP/2.0。
HTTP/2.0 引入多路复用:一个 TCP 连接可以同时承担多个 HTTP 请求/响应,做法是二进制分帧——把数据切成带流 ID 的小帧,不同请求的帧交错发送、接收端按 ID 重组;同时对头部做了压缩,重复的 HTTP 头部只发索引号,不用每次都发完整内容。这解决了应用层的并发问题,但 TCP 层要求字节按序到达,一个请求的帧没到齐会连累同一条连接上后面已经到达的请求——这就是 HTTP/2 的队头阻塞,由此引出了 HTTP/3.0。
HTTP/3.0 把传输层协议从 TCP 换成了基于 UDP 的 QUIC,让不同请求之间彻底解耦,每个请求只需要自己内部有序,请求之间不再相互影响。目前 HTTP/2 是主流,HTTP/3 主要在大厂和 CDN 落地——不少企业防火墙、中间件对 UDP 支持不好,导致它的普及还在推进中。
11. 讲一下 Cookie / Session / Token 的区别?
Cookie、Session、Token 都是给无状态的 HTTP 连接识别用户身份的方法。Cookie 把信息记在用户本地,服务器开销小,但可能被用户在本地篡改;Session 把用户信息存在服务器内存里,不能被客户端篡改,但服务器开销大;Token(JWT,JSON Web Token)把用户信息放在客户端,但加了服务器签名防篡改,服务器验证签名就能拿到用户身份。从存储上看,Session 要在服务端为每个在线用户存一条会话记录,每次请求都要查一次存储;Token 只需要服务端持有一把签名密钥,用户信息直接从 Token 里读,不用按用户建记录。
但 Cookie 和 Token 其实不在同一层:Cookie 是传输机制——浏览器自动在每次请求里带上的一小段数据,本质是个"信封";Token 是认证方案——一段携带用户信息 + 签名的数据,本质是"信的内容"。Token 可以装进 Cookie 里发送,也可以不用 Cookie,放在请求头的 Authorization 字段里手动带。真正该放在一起比较的,是 Session 方案和 Token 方案:
| Session 方案 | Token 方案 | |
|---|---|---|
| 传输方式 | Cookie 里装 Session ID | Cookie 或 Authorization 头里装 Token |
| 服务端 | 拿 ID 查存储 | 验签名,直接读 Token |
| 信息在哪 | 服务端 | 客户端(Token 里) |
12. 讲讲 DNS 查询?浏览器输入 URL 到页面显示,发生了什么?
DNS 查询就是通过域名找到对应的 IP 地址,按顺序依次查:①浏览器自己的 DNS 缓存,②系统 DNS 缓存和 hosts 文件。如果还查不到,会向本地 DNS 服务器发起递归查询——本地 DNS 服务器负责把整个解析过程走完、直接返回最终结果;本地 DNS 服务器自己如果也没有,就会对根 DNS、.com 顶级域 DNS、baidu.com 权威 DNS 发起迭代查询(以 www.baidu.com 为例),一层一层问下去,直到拿到 IP。
从浏览器输入 URL 到页面显示,完整过程是:DNS 查询拿到 IP → TCP 三次握手建立连接 → HTTPS 的话再走一次 TLS 握手 → 发送 HTTP 请求、拿到服务器响应 → 浏览器渲染页面。
13. 讲讲 ICMP 协议?常见 ICMP 报文的功能?
ICMP 是网络层协议,不是 TCP/UDP 那样的传输层协议,没有端口号。IP 数据包本身没有反馈机制,ICMP 就是用来给 IP 层补上差错报告和查询探测能力的。
| 报文类型 | 功能 |
|---|---|
| Echo Request/Reply | 查询探测,也就是 ping/pong |
| 目标不可达 | 细分网络不可达 / 主机不可达 / 端口不可达 |
| 超时 | TTL 减到 0 触发,traceroute 就是靠这个报文一跳一跳探出路径 |
| 重定向 | 路由优化 |
| 源站抑制 | 已废弃,现在的流量控制由传输层/TCP 拥塞控制负责 |
14. 讲讲 OSI 七层和 TCP/IP 四层?
OSI 是理论参考模型,实际用的是 TCP/IP 四层。OSI 的价值在于提供了统一的术语——"传输层""网络层"大家都听得懂在说什么——但真实协议栈是按 TCP/IP 四层跑的。
| OSI 七层 | 对应 TCP/IP 四层 | 功能 |
|---|---|---|
| 应用层 | 应用层 | 给用户程序提供网络服务接口,例如 SMTP 发邮件、DNS 查域名 |
| 表示层 | 应用层 | 管数据格式——编码转换、压缩、加密,确保一端发出的数据另一端能"看懂" |
| 会话层 | 应用层 | 管理会话的建立、维护、终止,比如"断点续传" |
| 传输层 | 传输层 | 端到端传输 |
| 网络层 | 网际层 | 跨网段寻址和路由,IP 地址,路由器工作在这层,ICMP 也在这层 |
| 数据链路层 | 网络接口层 | 同一网段内的帧传输,MAC 地址,交换机工作在这层 |
| 物理层 | 网络接口层 | 比特在物理介质上的传输 |
15. 讲讲 ARP 协议?
ARP 解决的问题是:同一局域网里,发送方知道接收方的 IP 地址,但还不知道对应的 MAC 地址,ARP 就是用 IP 地址换 MAC 地址的协议。
过程是:发送方在局域网内向广播地址 FF:FF:FF:FF:FF:FF 发送 ARP 请求,同一广播域里的所有主机都能收到。目标主机收到后对发送方单独回复;其它主机也能听到这次通信,但发现目标不是自己,直接丢弃。发送方收到回复后,把这个 IP-MAC 映射存进自己的 ARP 缓存表。
16. 讲讲子网划分和 IP 地址?
IPv4 地址有 32 位,通常用点分十进制表示,分成网络号和主机号两部分。比如一个 /26 子网,前 26 位是网络号,后 6 位是主机号。主机号全 0 代表这个网络本身,全 1 是这个网络的广播地址,两个都不能分给主机用,所以一个 /26 子网可用主机数是 2⁶ − 2 = 62 个。
私有 IP 地址有三个段,不会出现在公网上:
| 网段 | 类别 |
|---|---|
| 10.0.0.0/8 | A 类私有 |
| 172.16.0.0/12 | B 类私有 |
| 192.168.0.0/16 | C 类私有 |
17. 讲讲 NAT?
NAT 把内网的私有 IP 转换成公网 IP,通过 NAPT(端口映射)让多台内网主机共用同一个公网地址。IPv4 地址不够用,NAT 让大量设备可以共享少量公网 IP,代价是外部无法主动访问内网主机。
内网主机发包出去时,路由器把源地址从私网 IP 替换成自己的公网 IP,同时记一条映射;响应回来时,路由器按这条映射把目的地址换回对应的私网 IP,转发给内网主机。要解决"外部无法主动访问内网主机"这个问题,通常会用路由器端口转发、FRP 内网穿透这类技术。
18. 讲讲网络拓扑结构?
常见的网络拓扑结构有这四种:
| 拓扑 | 核心特征 | 优点 | 缺点 | 现状 |
|---|---|---|---|---|
| 星型 | 中心节点 | 故障隔离好、易扩展 | 中心单点故障 | 现在最常用 |
| 总线型 | 共享一条线 | 简单便宜 | 一断全断、带宽共享 | 基本淘汰 |
| 环型 | 首尾成环 | 延迟可预测 | 一断全断、扩展难 | 基本淘汰 |
| 网状型 | 多冗余路径 | 可靠性最高 | 成本最高 | 骨干网用 |
19. 讲讲 Socket 套接字?
Socket 是操作系统提供给应用程序的网络通信接口——操作系统把网络通信这件事封装成一套 API,应用程序只需要调几个函数就能收发数据,是应用层和传输层之间的那个窗口。
一个 Socket 连接由五元组唯一标识:(协议, 源 IP, 源端口, 目的 IP, 目的端口)。
TCP Socket 的通信流程:服务端创建 socket、绑定 IP 和端口、开始监听(ss -tlnp 能看到监听中的端口);客户端创建 socket,调用 connect() 发起连接,服务端 accept() 接受——三次握手就发生在 connect() 和 accept() 之间。连接建立后,双方用 recv()/send() 收发数据;用完之后两边调用 close(),四次挥手就发生在这时候。也就是说,前面几题讲的三次握手、四次挥手,落到代码层面就是这几个函数调用触发的。