以下为个人总结,图源大部分会来自网络和JavaGuide
网络分层模型
OSI七层模型
各层的常见协议
- 应用层 用户接口 HTTP, FTP, SMTP, DNS
- 表示层 数据格式转换 SSL/TLS, JSON, JPEG
- 会话层 会话管理 NetBIOS, RPC, SSH
- 传输层 端到端通信 TCP, UDP, QUIC
- 网络层 路由寻址 IP, ICMP, OSPF, BGP
- 数据链路层 帧传输 Ethernet, Wi-Fi, ARP
- 物理层 比特流传输 Ethernet, USB, 光纤
层间通信
发送方(封装):
应用层 → 表示层 → 会话层 → 传输层 → 网络层 → 数据链路层 → 物理层
Message → 加密 → 会话ID → TCP头 → IP头 → 帧头/尾 → 比特流
接收方(解封装):
物理层 → 数据链路层 → 网络层 → 传输层 → 会话层 → 表示层 → 应用层
比特流 → 帧解析 → 路由检查 → 端口分配 → 会话恢复 → 解密 → 用户数据
TCP/IP四层模型
- 应用层 直接为用户应用提供服务。HTTP、FTP、DNS、SMTP、SSH。
- 传输层 提供端到端的可靠或不可靠数据传输。TCP、UDP。
- 网络层 实现主机到主机的逻辑寻址和路由。封装数据为IP数据报,包含源/目的IP地址。IP、ICMP、IGMP。
- 网络接口层 负责在物理网络中传输数据帧。 以太网、Wi-Fi、ARP。
层间通信
发送端:
应用层 → 传输层 → 网络层 → 网络接口层
应用层数据 → [TCP头]+数据 → [IP头]+TCP段 → [帧头]+IP包+[帧尾] → 网络传输
接收端:
网络接口层 → 网络层 →传输层 → 应用层
网络传输 → 移除帧头/尾 → 移除IP头 → 移除TCP头 → 应用层数据
TCP相关
TCP的三次握手
- 第一次握手(SYN)
客户端发送 SYN=1(同步标志位),并随机生成一个初始序列号 seq=x。
目的:向服务器发起连接请求,并告知自己的初始序列号。
此时客户端进入 SYN_SENT 状态。 - 第二次握手(SYN+ACK)
服务器收到 SYN 后,回复 SYN=1 和 ACK=1(确认标志位),同时生成自己的初始序列号 seq=y,并确认客户端的序列号 ack=x+1。
目的:确认客户端的 SYN,并告知自己的初始序列号。
此时服务器进入 SYN_RCVD 状态。 - 第三次握手(ACK)
客户端收到 SYN+ACK 后,发送 ACK=1,确认服务器的序列号 ack=y+1,并携带自己的序列号 seq=x+1(因为第一次握手的 SYN 占用一个序号)。
目的:确认服务器的 SYN,完成双向连接建立。
此时双方进入 ESTABLISHED 状态,可以开始数据传输。
为什么需要三次握手?
- 确认双方的收发能力
第一次握手:服务器确认客户端能发。
第二次握手:客户端确认服务器能收和发。
第三次握手:服务器确认客户端能收。 - 防止历史重复连接初始化导致的资源浪费
如果是两次握手,服务器可能因收到延迟的旧 SYN 请求而误建连接,而客户端实际不需要。 - 同步初始序列号(ISN)
双方通过三次握手交换初始序列号,确保数据按序传输。
两次握手可以吗?
不行,服务器无法确认客户端是否收到自己的 SYN+ACK,可能导致半连接(服务器已就绪,客户端未就绪)。
四次握手可以吗?
如果改成四次握手(例如:客户端再发一个 ACK 确认服务器的 ACK),实际上是冗余的,因为第三次握手已经能确保双方通信正常,额外的确认不会带来更多好处,只会增加延迟。
四次挥手
- 第一次挥手(FIN)
客户端发送 FIN=1(终止标志位)和序列号 seq=u,表示客户端没有数据要发送了,请求关闭连接。
客户端状态:FIN_WAIT_1(等待服务器的 ACK)。
注意:此时客户端仍可以接收数据(因为 TCP 是全双工的)。 - 第二次挥手(ACK)
服务器收到 FIN 后,回复 ACK=1 和确认号 ack=u+1,表示已收到客户端的关闭请求。
服务器状态:CLOSE_WAIT(等待服务器应用层处理完剩余数据)。
客户端状态:收到 ACK 后进入 FIN_WAIT_2(等待服务器的 FIN)。 - 第三次挥手(FIN)
服务器处理完剩余数据后,发送 FIN=1 和 seq=v(可能是新的序列号),表示服务器也没有数据要发送了,请求关闭连接。
服务器状态:LAST_ACK(等待客户端的最终 ACK)。 - 第四次挥手(ACK)
客户端收到 FIN 后,发送 ACK=1 和 ack=v+1,表示确认服务器的关闭请求。
客户端状态:进入 TIME_WAIT(等待 2MSL 后彻底关闭)。
服务器状态:收到 ACK 后立即关闭连接。
为什么需要四次挥手?
- TCP 是全双工的,双方可以独立关闭连接:
- 客户端主动关闭(第一次挥手)→ 服务器确认(第二次挥手).
- 服务器可能还有数据要发送,不能立即关闭。
- 服务器处理完数据后主动关闭(第三次挥手)→ 客户端确认(第四次挥手)。
如果改成三次挥手?
- 服务器没有需要发送的消息时,可能会退化为三次挥手。
- 但是如果将所有的四次挥手改为三次挥手会导致:
- 服务器无法在确认客户端 FIN 后继续发送剩余数据。
- 可能造成数据丢失或不完整关闭。
为什么客户端需要 TIME_WAIT 状态?
- 等待 2MSL(Maximum Segment Lifetime,报文最大生存时间),确保:
- 服务器收到最后的 ACK(如果丢失,服务器会重传 FIN)。
- 让网络中残留的旧 TCP 报文失效,避免影响新连接。
- 默认 2MSL 时间:Linux 通常为 60 秒,Windows 为 4 分钟。
为什么服务器需要 CLOSE_WAIT 状态?
- 允许服务器处理剩余数据
当客户端发送 FIN 请求关闭连接时,服务器可能仍有数据需要发送(如未传完的文件、数据库查询结果等)。
CLOSE_WAIT 状态让服务器有机会继续发送剩余数据,而不是立即关闭连接。 - 确保应用程序正确释放资源
服务器需要等待应用程序(如 HTTP 服务器、数据库服务)处理完所有逻辑后,主动调用 close() 或 shutdown() 来发送自己的 FIN。
如果直接跳过 CLOSE_WAIT,可能导致:数据丢失(如未发送完的响应)和资源泄漏(如未关闭的文件句柄、数据库连接)。
CLOSE_WAIT 可能引发的问题
连接泄漏(大量 CLOSE_WAIT 堆积)
原因:服务器应用程序未正确调用 close(),导致 CLOSE_WAIT 状态长期存在。
后果:占用系统资源(文件描述符、内存)。最终导致服务器无法接受新连接(Too many open files)。
TCP的拥塞控制
慢启动、拥塞避免、快重传、快恢复
- 慢启动(Slow Start)
目的:初始阶段快速探测可用带宽。
规则:初始拥塞窗口(cwnd)通常为 1 MSS(Maximum Segment Size)。每收到一个 ACK,cwnd 指数增长(cwnd *= 2)。直到 cwnd 达到慢启动阈值(ssthresh)或发生丢包。 - 拥塞避免(Congestion Avoidance)
目的:接近网络容量时转为线性增长,避免激进引发拥塞。
规则:当 cwnd >= ssthresh 时,每 RTT(往返时间)cwnd += 1 MSS。增长速率从指数变为线性。 - 快速重传(Fast Retransmit)
目的:避免等待超时,快速恢复丢失的包。
规则:如果发送方收到 3 个重复 ACK(DupACK),立即重传丢失的包。无需等待超时计时器(RTO)到期。 - 快速恢复(Fast Recovery)
目的:在快速重传后避免重置 cwnd,保持较高吞吐量。
规则:发生快速重传后,ssthresh = cwnd / 2,cwnd = ssthresh + 3 MSS。每收到一个 DupACK,cwnd += 1 MSS(允许发送新数据)。收到新数据的 ACK 后,退出快速恢复,进入拥塞避免阶段。
TCP和UDP的区别
- TCP:面向连接,需三次握手建立可靠通道。确保数据可靠传输(确认、重传、排序)。通过滑动窗口和拥塞算法(如慢启动)优化传输。头部较大(至少20字节),延迟较高。字节流模式,无固定边界。
- UDP:无连接,直接发送数据包。不保证可靠性,可能丢包或乱序。无控制机制,全速发送。头部仅8字节,开销小,延迟低。:保留数据报边界,接收与发送一致。
TCP的粘包和拆包
-
粘包 (Packet Sticking)
定义:发送方发送的多个数据包被接收方当作一个数据包接收 -
拆包 (Packet Splitting)
定义:发送方发送的一个数据包被接收方拆分成多个数据包接收
产生原因
-
共同原因
- TCP是面向流的协议:没有消息边界概念,数据被视为字节流
- 滑动窗口机制:为提高效率可能合并或拆分数据包
-
粘包特定原因
- Nagle算法:合并小包减少网络传输次数
- 发送方快速连续发送小包:内核缓冲区可能合并
-
拆包特定原因
- 数据包大于MSS(最大报文段长度):TCP必须拆分
- 数据包大于接收缓冲区:应用层读取时可能分多次
- 网络设备限制:某些网络设备对包大小有限制
解决方案
- 消息定长法:每个消息固定长度,不足补位
- 分隔符法:使用特殊字符作为消息边界
- 长度前缀法:在消息前添加长度字段,通常使用固定字节表示长度(如4字节)
HTTP相关
HTTP/1.0
- 特点
短连接:每个请求/响应后关闭 TCP 连接(高延迟)。
无状态:每次请求需携带完整头部(无 Host 字段,无法支持虚拟主机)。 - 基础功能:支持 GET、POST、HEAD 方法。
- 缺点
性能差:频繁建立 TCP 连接,高延迟。
无 Host 头:无法区分同一 IP 的不同域名。
HTTP/1.1
- 改进
持久连接(Keep-Alive):默认复用 TCP 连接,减少握手开销。
管道化(Pipelining):允许连续发送多个请求(但响应必须按序返回,易阻塞)。
Host 头:支持虚拟主机(一个 IP 托管多个网站)。
分块传输(Chunked Encoding):支持流式传输大文件。
缓存控制:新增 Cache-Control、ETag 等头部。 - 缺点
队头阻塞(Head-of-Line Blocking):同一连接中,前一个请求未完成会阻塞后续请求。
头部冗余:每次请求携带重复头部(如 Cookie、User-Agent)。
HTTP/2
- 改进
二进制协议:替代文本格式,解析更高效。
多路复用(Multiplexing):单个连接并行传输多个请求/响应,解决队头阻塞。
头部压缩(HPACK):减少冗余头部大小。
服务器推送(Server Push):服务器可主动推送资源(如 CSS/JS)。
流优先级:允许设置请求优先级。 - 缺点
仍依赖 TCP:TCP 层的队头阻塞问题未解决(如丢包导致所有流等待重传)。
握手延迟:TLS 非强制(但主流浏览器要求 HTTPS)。
HTTP/3
- 改进
基于 QUIC 协议:运行在 UDP 上,替代 TCP。
0-RTT 握手:减少连接建立时间。
内置加密:默认使用 TLS 1.3。
解决 TCP 队头阻塞:每个流独立传输,丢包不影响其他流。
改进的多路复用:更高效的资源并行加载。 - 缺点
部署复杂:需要服务器和客户端支持 QUIC(如 Cloudflare、Nginx 已支持)。
UDP 被某些网络限制:可能被防火墙拦截。
HTTPS(HTTP + TLS/SSL)
- 特点
加密传输:使用 TLS/SSL 加密数据,防止窃听和篡改。
身份验证:通过证书验证服务器身份。
混合加密:非对称加密(握手) + 对称加密(数据传输)。 - 与 HTTP 的关系
可搭配任何 HTTP 版本(如 HTTPS/1.1、HTTPS/2)。
HTTP/3 默认加密(QUIC 内置 TLS 1.3)。
HTTPS 连接建立流程
- TCP 三次握手
客户端与服务器建立 TCP 连接(SYN → SYN-ACK → ACK)。 - TLS 握手
- Client Hello:客户端发送支持的加密算法列表和随机数(Client Random)。
- Server Hello:服务器选择加密算法,返回随机数(Server Random)和数字证书(含公钥)。
- 验证证书:客户端用 CA 公钥验证证书真实性。
- 密钥交换:
- 客户端生成 Pre-Master Key,用服务器公钥加密后发送。
- 双方通过 Client Random + Server Random + Pre-Master Key 生成会话密钥(对称加密密钥)。
- 加密通信
使用会话密钥加密传输 HTTP 数据,防止窃听和篡改。