Loading

网络

原文博客:https://nosae.top/posts/网络八股

tcp 报文

TCP 头格式

tcp 三次握手

TCP 三次握手
  • 客户端发送:SYN、随机序列号 x
  • 服务端发送:SYN、ACK、随机序列号 y、确认应答号 x+1
  • 客户端发送:ACK,可以携带数据

tcp 为什么不是两次握手

  • 防止旧的 SYN 建立连接:如果只有两次握手,那么服务端收到 SYN 后直接进入 established 状态(此时可以发送数据),然后返回 ack 给客户端,如果这个 SYN 是旧的,那么最终客户端发现不是想要的 ack,就会发送 rst 断开连接,那么服务端又要去断开已经建立好的连接,浪费资源。

    如果是三次握手,那么服务端不会直接进入 established。

  • 同步序列号:初始化序列号是最重要的,所以客户端发送初始序列号 x(第一次握手),客户端需要得知服务端已经收到并且服务端发送初始序列号 y(第二次握手),服务端需要得知客户端已经收到(第三次握手)

  • 如果第三次握手没发送到对端:本端也能直接发送数据,因为:

    image-20260222103405669

    但这不说明只需两次握手,三次握手和和两次握手的区别在于第二次握手后,对端是否直接进入 established。

tcp keepalive

keepalive 是 TCP 保鲜定时器,链接空闲时的心跳机制。

当超过一段时间之后,TCP 自动发送一个数据为空的报文给对方,如果对方回应了这个报文,说明对方还在线,链接可以继续保持,如果对方没有报文返回,并且重试了多次之后则认为链接丢失,没有必要保持链接。

tcp 四次挥手

客户端主动关闭连接 —— TCP 四次挥手
  • 客户端发送:FIN
  • 服务端发送:ACK
  • 服务端发送:FIN
  • 客户端发送:ACK
  • 客户端进入 TIME_WAIT,等待 2MSL(报文最大生存时间

其中,客户端一直收不到第三次握手 FIN 的话,客户端有两种情况:

  • 对于客户端调用 shutdown() 的情况,只关闭发送数据不关闭接收数据,因此客户端死等
  • 对于客户端调用 close() 的情况,同时关闭发送和接收数据,长时间收不到 FIN 就会主动 close

服务端一直收不到第四次握手 ACK 的话(在这之前处于 CLOSED_WAIT,并且服务端调用 close(),发送了 FIN),就会主动 close。

tcp 四次挥手客户端为什么要 TIME_WAIT

原因:

  • 等待历史连接的数据都已经在网络中自然消亡:如果没有 TIME_WAIT,假设此时客户端建立新的连接,并收到了上个连接中延迟到达的报文,并且序列号恰好在客户端的滑动窗口内,那么则接收到了错误的数据。
  • 保证服务端能正确关闭:等待足够的时间让 ACK 发到对面,如果由于网络原因服务端收不到的话就会重发 FIN,客户端收到后重置计时器为 2MSL,重传 ACK。如果没用 TIME_WAIT,客户端收到重传 FIN 的时候就会回一个 RST,虽然服务端也能关闭,但是是将其解释为错误,可能会使用户迷惑。

滑动窗口

滑动窗口用于提高发送数据的速率以及流量控制,每个窗口的单位为 1 个 MSS 大小的数据(一个 TCP 报文的最大长度,为了避免超过 MTU 造成分片,因为丢失一个分片就得重传整个 tcp 报文)

滑动窗口就是一个缓存空间,发送方主机在等到确认应答返回之前,必须在缓冲区中保留已发送的数据。如果按期收到确认应答,此时数据就可以从缓存区清除。这样一来,就不用发一个数据就等一个 ack,可以把窗口的数据连续发送了。

累计确认:ack = n 表示序号为 n 之前的报文都收到了,就算之前的 ack 都丢失也没关系。

窗口大小:窗口的大小由接收方的窗口大小来决定,由接收方告诉自己还有多少缓冲区可以接收数据,即发送端窗口不能大于接收端窗口

tcp Nagle 发送算法

解决发送数据量太小,头部占比很大,性价比很低(即 糊涂窗口综合症)。伪代码如下:

if 有数据要发送 {
    if 可用窗口大小 >= MSS and 可发送的数据 >= MSS {
    	立刻发送MSS大小的数据
    } else {
        if 有未确认的数据 {
            将数据放入缓存等待接收ACK
        } else {
            立刻发送数据
        }
    }
}

根据代码,为了避免 糊涂窗口综合症,需要:接收方「小窗口直接告诉发送方窗口为 0」+ 发送方开启 Nagle 算法

拥塞控制

为了有了流量控制后,还需要拥塞控制?只需要考虑最极端的情况,如果发送方和接收方的传输和接收能力都是无限的,那么瓶颈就出现在网络中,如果无限制地发送,网络只会越来越拥塞,因此需要拥塞控制。

拥塞控制也基于滑动窗口,并加入「拥塞窗口」的概念,因此 发送方窗口 = min(接收方窗口,拥塞窗口)

拥塞窗口如何增长:

  • 慢启动:每收到一个 ACK,拥塞窗口+1。拥塞窗口初始为 1,第一次收到 ACK,1+1 = 2,发送两个包,收到两个 ACK,2+2 = 4, 8, 16...慢启动每轮发送是指数增长的。
  • 拥塞避免:当慢启动超过阈值,每收到一个 ACK,拥塞窗口+1/cwnd,总的来看就是每轮发送才+1,而不是每收到一个 ACK+1.拥塞避免每轮发送是线性增长的。

拥塞窗口如何收缩(发生拥塞后):

  • 拥塞发生:发生超时重传的时候,慢启动阈值设为 cwnd/2,cwnd 设为 1
  • 快速回复:发生快速重传的时候,慢启动阈值设为 cwnd/2,cwnd 设为慢启动阈值

tcp 粘包解决

  • 特殊字符作为消息结束符
  • 自定义消息结构,比如在头部定义一个消息长度

tcp 已经处于 established 服务端收到新的 SYN

服务端会回复属于它的连接的 ack,这样客户端发现不是自己想要的 ack,就会回一个 rst,然后服务端就会释放这个连接

MSS(Maximum Segment Size)

MSS 只计算 TCP 有效载荷(Payload),不包括 TCP 头部和 IP 头部。

  • 计算公式:

    通常情况下,

    \[MSS = MTU - IP \text{ Header} - TCP \text{ Header} \]

  • 在标准的以太网环境中:

    • MTU (最大传输单元) 通常是 1500 字节。
    • IP 头部 通常是 20 字节。
    • TCP 头部 通常是 20 字节。
    • 因此,常见的 MSS = 1460 字节 (\(1500 - 20 - 20\))。

tcp 和 udp

tcp 可靠而 udp 不可靠具体体现在:udp 没有重传、不保证包的到达顺序、没有流量控制、拥塞控制。只保证首部+数据的校验

对于流量控制这一点,除了窗口之外还要知道发送和接收缓存,特别是接收缓存,当窗口满的时候 tcp 会通过窗口机制通知对方窗口关闭,保证不会溢出,如果对方无视窗口大小,则接收方会直接丢弃;而 udp 没有窗口控制机制,直接选择丢弃。

http 和 https

http1.1 性能:

  • 长连接:避免每次都建立 tcp 连接
  • 管道传输:客户端可以并行发送
  • 响应队头阻塞(缺点):响应方必须按顺序处理并返回一个请求再处理下一个请求

http 不安全在于会出现:

  • 窃听:明文传输
  • 伪装:不验证身份
  • 篡改:报文不校验

httpS 解决 http 的不安全:

  • 数据加密:防止窃听
  • 证书:防止伪装
  • 数据校验:防止篡改

tls 握手协议(RSA)

  1. ClientHello:客户端通过发送 "client hello" 消息向服务器发起握手请求,该消息包含了 客户端所支持的 TLS 版本、密码组合以供服务器进行选择、还有一个 "client random" 随机字符串
  2. ServerHello:服务器发送 "server hello" 消息对客户端进行回应,该消息包含了 数字证书、服务器选择的密码组合、"server random" 随机字符串
  3. 验证:客户端对服务器发来的证书进行验证(检查数字签名、证书链、证书有效期、证书撤回状态),确保对方的合法身份,并取出证书中的公钥,然后客户端发送公钥加密的另一个随机字符串 "premaster secret (预主密钥)"
  4. 双方生成对称加密密钥:此时双方都拥有了上述的三个随机数,双方用这三个随机数生成共享密钥 KEY
  5. 双方就绪:双方 用 KEY 加密「finish 信号」并发送给对方
  6. 握手完成

其中第 3 步中的验证证书,数字签名是对证书进行做摘要,并用 CA 的私钥对摘要加密得到的,数字签名目的是保证证书的没被篡改。

验证证书实际上是验证 证书链

img
  1. 客户端收到服务端证书(比如 baidu.com),发现不是 根证书(根证书是预先加载到操作系统/浏览器,一定受信任的),则查看证书颁发机构是图中的「中间证书」,然后向 CA 请求该证书
  2. 请求到「中间证书」后,发现不是根证书,则查看证书颁发机构是图中的「根证书」,如果该根证书是提前安装到操作系统受信任的,那么 使用该根证书的公钥验证中间证书,如果验证通过,那么中间证书就是可信的(没被篡改)
  3. 由于此时中间证书可信了,那么 使用中间证书的公钥验证服务器证书,如果验证通过,那么服务器证书就是可信的(没被篡改)

整个握手过程,加密/摘要算法一般是:

  • 非对称加密:RSA 等
  • 对称加密:AES、3DES 等
  • 摘要:SHA-256、SHA-1、MD5 等

tls 记录协议

tls 握手协议用于连接建立(主要是生成密钥)。

tls 记录协议用于后续通信,主要负责消息(HTTP 数据)的压缩、加密、解密认证。

img
  • http 明文数据分割为消息片段,然后对片段压缩
  • 计算压缩片段的 MAC 值(摘要),保证消息不被篡改。并加上片段的编码,防止重放攻击
  • 最后加上数据的元信息

http1.1

相对于 http1.0:

  • 长连接:解决每次都建立 tcp 连接
  • 管道传输:解决发送方队头阻塞

http2

相对于 http1.1:

  • 头部压缩:以 kv 形式缓存头部在服务端,客户端只需要发送对应索引号
  • 二进制格式:头部和数据都用二进制格式
  • Stream:不同 stream 可以并发传输,用 stream ID 保持消息的顺序
  • 服务器主动推送:比如渲染页面需要 html+css,只需要一次请求,返回 html,并且还能主动推送 css

http3(QUIC)

相比 http2,虽然 http2 使用了 stream 解决了 http1 的队头阻塞,但依然存在 tcp 层面的队头阻塞,比如:stream2 stream3 已经完全到达,但是 stream1 还未到达,tcp 层认为 stream2 stream3 还不能被应用层接收,因此导致队头阻塞

http3 使用了 udp,在应用层解决 tcp 存在的问题:

  • 无队头阻塞:依然使用 stream,但 stream 由多个 udp 包组成,某个 stream 的 udp 包丢失不会影响其他 stream 的接收,解决了队头阻塞
  • 更快的连接建立:QUIC 包含了 tls,因此 QUIC 握手可以包含 tls 握手所需的信息,减少了建立连接的通信次数
  • 连接迁移:tcp 连接基于四元组,而 QUIC 连接基于连接 id,服务端和客户端各自选择一组 id 标识自己,就算网络 IP 发生变化(比如 wifi 变成 4g),但由于是同一个设备,只要设备支持 quic,保存了 id 和 tls 密钥等,继续用他们来通信即可,做到无缝切换。

http 和 rpc

  • 服务发现:http 的服务发现比如 dns(根据域名发现 ip)。rpc 的服务发现比如 etcd(根据服务找到 ip 和端口),区别不大
  • 底层连接:http 的 keepalive 可以复用 tcp 连接。rpc 也可以基于 tcp,并使用连接池复用 tcp 连接
  • 传输内容:rpc 可以自定义消息结构,比如 protobuf,自定义除冗余字段、压缩字段等,效率更高

综上,rpc 更多用于内部服务之间的通信,对外则是使用统一标准的比如 http(B/S 架构)

http 和 websocket

由于 http 设计为一问一答的协议,ws 出现之前基于 http 的「伪」服务端推送:

  • 轮询:客户端定时请求,服务端查询数据后马上返回
  • 长轮询:客户端请求,服务端挂起这个请求,直到检测到有新数据后再返回,要求服务端有挂起多个请求的能力

ws:

# 请求
Connection: Upgrade
Upgrade: WebSocket
Sec-WebSocket-Key: T2a6wZlAwhgQNqruZ2YUyg==\r\n
# 响应
HTTP/1.1 101 Switching Protocols\r\n
Sec-WebSocket-Accept: iBJKv/ALIW2DobfoA4dmr3JHBCY=\r\n
Upgrade: WebSocket\r\n
Connection: Upgrade\r\n

响应状态码 101 switching protocols

ws 报文结构,使用「头+数据」解决 tcp 粘包:

图片

CDN(用户点击 URL 时发生的步骤)

image-20241122004728841

  1. 本地 DNS 系统 解析,DNS 系统会最终将域名的解析权交给 CNAME 指向的 CDN 专用 DNS 服务器
  2. CDN 的 DNS 服务器将 CDN 的全局负载均衡设备 IP 地址返回用户
  3. 用户向 CDN 的全局负载均衡设备发起内容 URL 访问请求,CDN 全局负载均衡设备根据用户 IP 地址,以及用户请求的内容 URL,选择一台用户所属区域的 区域负载均衡设备,告诉用户向这台设备发起请求
  4. 域负载均衡设备会为用户选择一台合适的 缓存服务器 提供服务,返回其 ID 地址给用户
  5. 用户向缓存服务器发起请求,如果缓存服务器没有用户要的资源,那么这台服务器就要向它的上一级缓存服务器请求内容,直至追溯到网站的源服务器将内容拉到本地缓存,最终返回资源给用户

访问 url 发生什么

https://xiaolincoding.com/network/1_base/what_happen_url.html

  1. DNS:查询 DNS 缓存/DNS 服务器得到域名对应的 IP
  2. TCP:与 ip: port 建立 tcp 连接,分割 http 报文不超过 MSS
  3. IP:根据路由表确定下一跳的 ip 地址(网关),如果 ip 地址为空(网关为空)说明已经到达了终点
  4. MAC:mac 地址才能唯一标识一个设备,因此使用 arp 协议广播获取拥有该下一跳 ip 的设备 mac 地址,然后加上 mac 头、起始分界符、校验和,发送到交换机
  5. 交换机:交换机根据目标 mac 查询 mac 地址表,将信号发送到对应的端口,如果找不到的话就广播到所有端口,接收信号的设备会检查目的 mac 是否自己,如果不是则丢弃,否则响应一个 mac 地址给交换机,交换机将记录端口与该 mac 地址的映射

参考

https://strikefreedom.top/archives/tcp-connection-management-and-data-transmission-in-depth

https://www.xiaolincoding.com/

posted @ 2026-02-21 17:20  NOSAE  阅读(5)  评论(0)    收藏  举报