计算机网络相关知识,用作备忘录,以备查阅。

01 网络协议的分层结构

OSI 分层模型:

  • 自上而下分为应用层,表示层,会话层,传输层,网络层,数据链路层,物理层七层
  • 会话层,表示层,应用层称为资源子网(用来数据处理),物理层,数据链路层,网络层称为通信子网(实现数据通信),传输层是通信子网和资源子网的通信接口

五层模型:

  • 应用层:进行应用数据处理,封装后交给传输层
  • 传输层:对应用层数据进行封装,如 TCP 和 UDP 协议
  • 网络层:对传输层数据封装,如 IP 协议,其会提供寻址能力
  • 数据链路层:每台设备有唯一的 MAC 地址(通过 ARP 协议),为网络层提供链路级别传输的服务
  • 物理层:提供二进制传输的服务

网络包封装过程:

64002

02 数据在网络中的封装

一个常见的问题:当在浏览器中输入网址后,到网页显示,其间发生的过程?

  1. 浏览器对 URL 进行解析
  2. 查询 URL 中的服务器对应的 IP 地址,客户端对本地 DNS 服务器采用递归查询,本地 DNS 服务器对 DNS 根服务器进行迭代查询
  3. 浏览器通过调用 Socket 库,来委托操作系统的 TCP/IP 协议栈进行相应的数据操作
  4. TCP 封装:通过 MSS 分割应用层数据,添加对应包头,主要包含源端口号,目的端口号,序号,确认序列,状态位(SYN,ACK,RST,FIN),在传输数据之前,需要先进行三次握手
  5. IP 封装:主要包含源 IP 地址和目标 IP 地址,如果服务器存在多个网卡,需要通过网络和子网掩码来确认对应的 IP
  6. MAC 封装:两点间的传输,主要包含发送方 MAC 地址和接收方目标 MAC 地址,协议类型主要有 IP 协议和 ARP 协议,ARP 协议会在以太网中用广播的方式来询问 IP 地址对应的 MAC 地址
  7. 网卡:出口,将数字信息转换为电信号,网卡驱动从 IP 模块获取到包之后,会将其复制到网卡内的缓存区中,接着会其开头加上报头和起始帧分界符,在末尾加上用于检测错误的帧校验序列
  8. 交换机:二层网络设备,其端口不包含 MAC 地址,根据 MAC 地址表查找 MAC 地址,然后将信号发送到相应的端口
  9. 路由器:三层网络设备,路由器的各个端口具有 MAC 地址和 IP 地址,每次传输都需要修改 MAC 地址,但是源 IP 和目标 IP 始终是不会变动
  10. 请求达到服务器端:解包,获取应用层数据,返回相应的响应报文
  11. 响应到达客户端:解包出来响应的 html 数据交给浏览器进行渲染
  12. 客户端断开连接:通过 TCP 四次握手(亦可通过 keep-alive 保持连接)

网络包报文图解:

图片

03 ICMP 协议及其应用

ICMP 协议:

  • 功能:确认 IP 包是否成功送达目标地址、报告发送过程中 IP 包被废弃的原因和改善网络设置
  • 报文格式:被封装在 IP 包中,主要包含类型和代码,主要类型有
    • 查询报文类型:回送应答和回送请求
    • 差错报文类型:目标不可达,原点抑制消息,重定向消息,超时消息(TTL)

ping 命令的过程:

  1. 构建回送请求数据包,此时类型为 8,序号为 1,同时插入发送时间
  2. 系统根据回送请求数据包构建 IP 包,修改协议为 1,表示 ICMP
  3. 将 IP 包交给下层,将其发送到目标主机
  4. 目标主机获取到回送请求数据包,构建对应的回送响应数据包,此时类型为 0,委托 IP 层发送给源主机

traceroute 命令实现(通过 UDP 或者 ICMP):

  • 故意设置特殊的 TTL,来追踪去往目的地时沿途经过的路由器
  • 故意设置不分片,从而确定路径的 MTU

ICMP 报文格式:

图片

04 HTTP 协议的变迁

HTTP 状态码:

  • 1xx:表示目前是协议的中间状态
  • 2xx:成功,如 200,204,206
  • 3xx:重定向,如 301,302,304
  • 4xx:客户端错误,如 400,403,404
  • 5xx:服务器错误,如 500,501,502,503

HTTP 首部常见字段:Host,Content-Length,Connection,Content-Type,Content-Encoding

HTTP 特性:

  • 优点:报文格式简单,灵活和易于扩展,应用广泛和跨平台
  • 不足:无状态双刃剑,明文传输不安全

HTTPS:在原来的 TCP 和 HTTP 层之间加入了 SSL/TLS 层,保证信息的加密传输

  • 混合加密保证了信息的机密性
  • 摘要算法用来实现完整性
  • 通过数字证书的方式保证服务器公钥的身份,解决冒充的风险

HTTP 协议的演变:

  • HTTP/1.1:增加了长连接,支持管道网络传输,但是可能会存在队头阻塞问题
  • HTTP/2:基于 HTTPS,保障安全性,头部压缩,二进制格式,支持数据流,多路复用,服务器推送
  • HTTP/3:在 HTTP/2.0 多路复用时,如果某个请求发生丢包,会触发 TCP 重传机制,而阻塞其他请求,HTTP/3 基于 UDP,上层的 QUIC 协议可以实现类似 TCP 的可靠性传输,同时 QUIC 合并了 TCP 三次握手和 TLS 四次握手

HTTP 各种协议栈:

图片

05 TLS 协议

TLS 握手过程:

  • Client Hello:发送支持的密码套件列表,以及生成的随机数等
  • Server Hello 消息给出随机数和选择的密码套件,Server Certificate 消息给出服务器的数字证书,Server Hello Done 结束
  • 客户端进行证书验证,可能涉及到证书链,验证证书通过检查证书的签名
  • Change Cipher Key Exchange 消息加密 pre-master,将其发送到服务器;Change Cipher Spec 消息用于告诉服务端开始使用加密方式发送消息;Finishd 消息会把之前的消息做个摘要,防止篡改
  • 服务器发送 Change Cipher Spec 和 Encrypted Handshake Message 消息给客户端

RSA 算法:用于进行密钥协商,但是不支持前向保密,一旦服务端的私钥泄漏了,过去被第三方截获的所有 TLS 通讯密文都会被破解,可以改用 ECDHE 密钥协商算法。

HTTPS 建立连接的过程:

图片

06 HTTPS 优化

HTTPS 性能损耗:

  • TLS 握手过程最长需要 2 RTT
  • 后续的应用数据需要使用对称加密密钥进行加密和解密

HTTPS 优化方式:

  • 硬件优化:选择支持 AES-NI 特性的 CPU,否则可以将对称加密算法改为 ChaCha20
  • 软件升级:升级 Linux 内核,更新 OpenSSL
  • 协议优化:
    • TLS 1.2 可以使用 ECDHE 密钥交换算法,客户端可以在 TLS 协议的第 3 次握手后,第 4 次握手前,可以发送加密的应用数据,减少了 1 个 RTT
    • TLS 1.3 把 Hello 和公钥交换这两个消息合并成了一个消息,于是这样就减少到只需 1 RTT 就能完成 TLS 握手
  • 证书优化:
    • 选择椭圆曲线(ECDSA)证书,相同安全强度下,其密钥长度比 RSA 短的多
    • 证书验证协议优化:CRL,OCSP(CA 服务器成为瓶颈),OCSP Stapling
  • 会话复用:不仅不具备前向安全,而且有重放攻击的风险,设置合理的过期时间
    • Session ID:双方缓存会话密钥,并用唯一的 Session ID 标识,服务器的内存压力增大
    • Session Ticket:服务器会加密会话密钥作为 Ticket 发给客户端,交给客户端缓存该 Ticket
    • Pre-shared Key:TLS 1.3 使用,原理和 Ticket 类似,只不过在重连时,客户端会把 Ticket 和 HTTP 请求一同发送给服务端

ECDHE 交换算法:

图片

07 密钥交换算法

HTTPS 中常见的密钥交换算法有 RSA 和 ECDHE 算法,由于前者不支持前向安全性,ECDHE 被广泛使用。

ECDHE 算法发展:

  • DH 算法:基于离散对数
  • DHE 算法:让双方的私钥在每次密钥交换通信时,都是随机生成的、临时的
  • ECDHE 算法:在 DHE 算法的基础上利用了 ECC 椭圆曲线,用更少的计算量计算出公钥和会话密钥

DH 算法过程:

64003

08 HTTP/1.1 优化

HTTP/1.1 优化方式:

  • 减少 TCP 握手时间:使用 KeepAlive 将短连接修改为长连接,注意其不同于 TCP 的 keepalive
  • 避免发送 HTTP 请求:使用缓存
  • 减少 HTTP 请求次数:减少重定向请求次数,合并多个请求,懒加载
  • 减少 HTTP 响应的数据大小:无损压缩和有损压缩

客户端缓存过程:

图片

09 HTTP/2 带来的优化

HTTP/1.1 面临的问题:

  • HTTP 头部巨大并且存在重复
  • 并发连接有限,如 Chrome 浏览器最大并发数是 6
  • HTTP/1.x 协议导致的队头阻塞问题
  • 不支持服务器推送

HTTP/2 向下兼容 HTTP/1.1,其特性:

  • 头部压缩 HPACK 算法:由静态字典,动态字典和 Huffman 编码构成
  • 二进制帧:对于一条 HTTP 响应,划分成了两个帧(HEADER + DATA)来传输
  • 并发传输:多个 Stream 复用一条 TCP 连接,达到并发的效果,不同 Stream 的帧是可以乱序发送的,同一 Stream 内部的帧必须是严格有序的,还可以设置优先级
  • 服务器主动推送:如在客户端请求 index.html 的时候主动推送 index.js 和 index.css
  • TCP 协议导致的队头阻塞问题:TCP 需要等待前面的字节到达内核后才能将其传输到应用层

Stream 示意图:

图片

10 HTTP/3 带来的优化

HTTP/2 缺点:

  • TCP 队头阻塞
  • TCP 和 TLS 的握手时延需要 3 RTT
  • 网络迁移后需要重新进行连接

HTTP/3 使用 UDP 传输协议,在应用层使用 QUIC 保证其可靠:

  • 无 TCP 产生的队头阻塞,如果 QUIC 连接中某个数据包丢失了,其只会阻塞该流,其他流不受影响
  • 更快的连接建立:QUIC 可以在 1 个 RTT 内同时完成建立连接与密钥协商,在第二次连接时,应用数据包可以和 QUIC 握手信息(连接信息 + TLS 信息)一起发送,达到 0-RTT 的效果
  • 只需要通过连接 ID 来标记通信的两个端点,不涉及 IP 地址,切换网络后(弱网环境)不需要重新连接
  • HTTP/3 的 QPACK 通过两个特殊的单向流来同步双方的动态表,解决 HTTP/2 的 HPACK 队头阻塞问题
  • QUIC 使用 UDP 传输,大部分路由器在网络繁忙的时候,会丢 UDP 包

HTTP 不同协议的连接示意图:

图片

11 TCP 三次握手和四次挥手

TCP 基本认识:

  • TCP 是面向连接的、可靠的、基于字节流的传输层通信协议
  • 头部格式包含:序列号,确认应答号,控制位
  • 最大 TCP 连接数:客户端 IP 数 × 客户端端口数,但实际受限于文件描述符和内存大小
  • TCP 和 UDP 区别:连接,服务对象,可靠性,拥塞控制和流量控制,首部开销,应用场景

TCP 三次握手:

  • 第三次握手是可以携带数据的,前两次握手是不可以携带数据的

  • 为什么是三次握手:

    • 三次握手才可以阻止历史重复连接的初始化(防止服务器一收到 SYN 包直接进入 ESTABLISH 状态,消耗资源)
    • 三次握手才可以同步双方的初始序列号
    • 三次握手才可以避免资源浪费
  • IP 层也会进行分片,为啥 TCP 层还需要 MSS:如果只交给 IP 层分片,发生丢包后,TCP 会重传整个 TCP 报文,而如果 TCP 采用 MSS,则只需要传送 MSS 单位的数据即可

  • SYN 攻击:通过伪造 SYN 报文,占满服务器的 SYN 接收队列,使得服务器不能正常工作

TCP 四次挥手:

  • 需要四次挥手:客户端发送 FIN 报文表示客户端不再发送数据给服务器,服务器接收到 FIN 报文后,可能还需要发送响应数据给客户端,因此服务器 ACK 和 FIN 报文不会一起发送给客户端
  • 为什么需要 TIME_WAIT 状态:
    • 防止 TCP 重连后接收到旧的数据包
    • 保证最后的 ACK 能让被动关闭方接收,从而帮助其正常关闭
  • 为什么 TIME_WAIT 需要 2MSL:保证最后的 ACK 可能丢失时,依旧能够收到服务器的 FIN 报文
  • TIME_WAIT 过高的危害:消耗内存资源和端口资源

TCP 三次握手过程图:

图片

TCP 四次挥手过程图:

图片

12 TCP 相关机制

TCP 重传机制:

  • 超时重传:超时重传时间 RTO 的值应该略大于报文往返 RTT 的值,发生重传时,会加倍 RTO
  • 快速重传:收到三次相同 ACK,触发重传
  • SACK:在 TCP 头部选项中加入 SACK,告知发送方哪些数据已经被接收
  • D-SACK:使用了 SACK 来告诉发送方有哪些数据被重复接收了,让发送方知道是丢数据包还是 ACK 包

滑动窗口:

  • 改善 TCP 每发送一个数据包就进行一次确认应答的性能
  • 接收窗口和发送窗口构成,接收窗口的大小是约等于发送窗口的大小

流量控制:

  • 通过滑动窗口,可以让发送方根据接收方的实际接收能力控制发送的数据量
  • 如果窗口大小为 0 时,就会阻止发送方给接收方传递数据,只要 TCP 连接一方收到对方的零窗口通知,就启动持续计时器,如果超时,就会发送窗口探测报文
  • 糊涂窗口综合症:通过 TCP 发送少量数据给接收方,可以
    • 让接收方不通告小窗口给发送方
    • 让发送方避免发送小数据

拥塞控制:

  • 防止网络出现拥堵时,发送方还继续发送大量数据包,导致网络更加拥堵

  • 引入拥塞窗口后,发送窗口的值为 swnd = min(cwnd, rwnd),只要网络没有拥塞,cwnd 就会增大,反之,cwnd 就会减少

  • 一旦发生了重传,就会认为网络出现了拥塞,拥塞算法如下

    • 慢启动:cwnd 呈现指数型增长
    • 拥塞避免:一旦到达慢启动门限,cwnd 呈现线性增长
    • 拥塞发生:
      • 超时重传:ssthresh = cwnd / 2,cwnd = 1,也就是进入慢启动
      • 快速重传 + 快速恢复:ssthresh = cwnd / 2,cwnd = ssthresh + 3,进入拥塞避免

    img

发送窗口和接收窗口:

图片

图片

13 IP 层

IP 层工作在第三层,实现两个主机间的通信,而 MAC 作用是实现两个直连设备之间的通信

IP 地址的分类:

  • 传统分类:A,B,C,D,E,但存在同一网络下没有地址层次,不能很好的与现实网络匹配等问题
  • CIDR:表示为 a.b.c.d/x ,前 x 位表示网络号,后 32 - x 位表示主机号,类似子网掩码,还可以对其进行子网划分

IP 分片和重组:当 IP 数据包超过 MTU 大小时,IP 数据包就会被分片,但是由于分片后的 IP 数据包只能在目标主机进行重组,一旦丢失某个分片数据,则需要重传整个 IP 数据包,为此引入了 TCP 的 MSS

IPv4 和 IPv6 区别:

  • 地址位数及其表示方法
  • IPv6 即使没有 DHCP 服务器也可以自动分配 IP 地址
  • IPv6 包头固定长度为 40 字节,提高了传输性能
  • IPv6 能够防止线路窃听,提高了安全性

IP 相关协议:DNS,ARP,RARP,DHCP,NAT,NAPT,ICMP

14 TCP 抓包分析

抓包工具:tcpdump 和 wireshark

抓包到的 TCP 挥手是三次:当服务器受到客户端的 FIN 时,如果之后没有数据发送给客户端,就会合并 ACK 和 FIN 报文

TCP 三次握手异常:

  • 第一次的 SYN 包丢失:RTO 指数增长,tcp_syn_retries
  • 第二次的 SYN + ACK 包丢失:RTO 指数增长,tcp_syn_retries,tcp_synack_retries
  • 第三次的 ACK 丢包:RTO 指数增长,tcp_synack_retries,tcp_retries2

TCP 四次挥手异常:

  • 第一次的 FIN 包丢失:RTO 指数增长,tcp_orphan_retries
  • 第三次的 FIN 包丢失:RTO 指数增长,tcp_orphan_retries
  • 第一次或者第四次 ACK 包丢失:不会重传 ACK 包,而是重传 FIN 包

TCP Fast Open:第一次 TCP 连接时给客户端发送 Cookie,接下来的连接就可以直接带上 Cookie 和对应的 HTTP 请求,达到 1 RTT 的效果

TCP 延迟确认和 Nagle 算法:用于增加整个报文的有效负载

  • 延迟确认:没有携带数据的 ACK,其有效负载也是很低的,通过等待一定时间再发送 ACK 给客户端
  • Nagle 算法:没有已发送未确认报文时,立刻发送数据;存在未确认报文时,直到没有已发送未确认报文或数据长度达到 MSS 大小时,再发送数据
  • 不能同时使用延迟确认和 Nagle 算法,其会造成额外的时延

TCP Fast Open:

图片

15 TCP 半连接队列和全连接队列

当服务端受到客户端的 SYN 请求后,内核会把该连接存储到半连接队列;当服务端受到客户端的 ACK 响应后,内核会把连接从半连接队列移除,然后创建新的完全的连接,将其放入全连接队列中

TCP 全连接队列溢出:

  • 后续请求报文或者被丢弃,或者被发送 RST 报文,由 tcp_abort_on_overflow 控制
  • 增大全连接队列:全连接队列最大值 min(somaxconn, backlog)

TCP 半连接队列溢出:

  • 半连接队列最大值 max_qlen_log = min(min(somaxconn, backlog), max_syn_backlog) × 2
  • 当开启了 tcp_syncookies 功能后可以在不使用 SYN 半连接队列的情况下建立连接
  • 防御 syn 攻击:开启 tcp_syncookies 功能,增大半连接队列,减少 SYN + ACK 重传次数

TCP 连接队列示意图:

图片

16 TCP 调优

TCP 三次握手:

  • 调整 SYN 报文的重传次数:tcp_syn_retries
  • 调整 SYN 半连接队列长度:tcp_max_syn_backlog,somaxconn,backlog
  • 调整 SYN + ACK 报文的重传次数:tcp_synack_retries
  • 调整 accept 队列长度:somaxconn,backlog
  • 绕过三次握手:tcp_fastopen

TCP 四次挥手:

  • 调整 FIN 报文重传次数:tcp_orphan_retries
  • 调整 FIN_WAIT2 状态的时间:tcp_fin_timeout
  • 调整孤儿连接上限:tcp_max_orphans
  • 复用 time_wait 状态的连接:tcp_tw_reuse

TCP 传输优化:

  • 扩大窗口:tcp_window_scaling
  • 调整发送缓冲区范围:tcp_wmem
  • 调整接收缓冲区范围:tcp_rmem
  • 调整内存范围:tcp_mem

17 TCP 和 UDP

TCP 是面向字节流的协议,而 UDP 是面向报文的协议:

  • 面向报文指的是每个 UDP 报文就是一个用户消息的边界,在收到 UDP 报文后,可以读取并解析,因此需要应用层控制报文的大小

  • 面向字节流则是指操作系统可能会对用户的消息进行拆分,使其满足 MSS 等限制,这样可能会造成粘包问题,可以以下方式解决:

    • 固定长度的消息

    • 特殊字符作为边界

    • 自定义消息结构

18 TCP 和 HTTP 的 keepalive 机制

HTTP 的 Keep-Alive 也叫 HTTP 长连接,其目的为减少 HTTP 短连接中每次请求都需要 TCP 三次握手和四次挥手的开销,同时也使得 HTTP/1.1 流水线技术得以实现

TCP 的 Keepalive 也叫 TCP 保活机制,当 TCP 层长时间没有数据交互时,内核为了确保该连接是否还有效,就会发送探测报文,来检测对方是否还在线,然后来决定是否要关闭该连接

19 TCP 异常

假设:客户端和服务端通过 TCP 连接,并且没有开启 TCP 的 keepalive,在下列情况下:

  • 一直没有数据交互下:
    • 主机崩溃(掉电):服务器对此是无法感知的,服务端的 TCP 连接将会一直处于 ESTABLISHED 连接状态,直到重启
    • 进程崩溃(kill):操作系统会自动发送 FIN 报文,进行四次挥手
  • 存在数据交互下,客户端主机崩溃:
    • 迅速重启:在收到服务器重传的报文时,会回复 RST 报文,重置 TCP 连接
    • 不重启:服务器端一直进行报文重传,由 tcp_retries2 控制

如果客户端在 TIME_WAIT 状态下,收到来自服务端的数据包时,客户端的内核会发送该数据包的 ACK 确认报文(防止服务端重传),然后直接丢掉该数据包,具体过程如下:

图片

如果客户端在 FIN_WAIT_2 状态下,FIN 报文和服务器响应的数据乱序了,也就是如果客户端收到乱序的 FIN 报文,那么就被会加入到乱序队列,并不会进入到 TIME_WAIT 状态,等 FIN 之前的数据都被接收后自动进入 TIME_WAIT 状态:

图片

20 常用的网络命令

远程登陆和传输:ssh,scp

查看本地网络状态:ifconfig,netstat

网络测试:ping,telnet

DNS 查询:host,dig

HTTP 请求:curl

21 信息风险与防范措施

HTTP 明文传输,并且没有身份认证,所以存在以下风险:

  • 窃听风险:中间人可以截获报文查看其中信息,通过对称加密防范
  • 篡改风险:中间人可以截获报文并且修改后发送给收件人,可以用哈希函数来计算出内容的哈希值
  • 冒充风险:虽然使用哈希可以保证报文不被篡改,但是不能防止中间人重新构建一个报文发送给收件人的情况。为此可以采用非对称加密方法,通过私钥加密,公钥解密的方式,来确认消息的身份,注意此时加密的是内容的哈希值。通过 CA 保证信任链的真实可靠,防止中间人伪造公私钥

数字证书签发和验证流程:

64001

22 TLS 协议

TLS 1.2 和 TLS 1.3 协议的区别:

  • TLS1.2 需要四次握手,握手完成后才能发送数据;TLS 1.3 只需要两次握手过程
  • TLS 1.3 会话恢复时握手过程只需要 0 RTT,通过 pre_shared_key 实现

通常,不管 TLS 握手次数如何,都得先经过 TCP 三次握手后才能进行;但是如果在 TCP Fast Open + TLS 1.3 情况下,在第二次以后的通信过程中,TLS 和 TCP 的握手过程是可以同时进行的

TLS 会话恢复:

64006

23 CDN

CDN 用于解决远距离导致的网络访问延迟问题,通常用于加速一些静态资源,CDN 加速策略分为:

  • 推模式:当用户就近访问的 CDN 节点没有缓存请求的数据时,CDN 会主动从源服务器下载数据,并更新到这个 CDN 节点的缓存中
  • 拉模式:也称为 CDN 预热,通过 CDN 服务提供的 API 接口,把需要预热的资源地址和需要预热的区域等信息提交上去

CDN 通过 GSLB 服务器找到最近的 CDN 服务器:

64007

24 第三方 TCP 工具断开连接的实现

如果一个已经建立的 TCP 连接,客户端中途宕机了,而服务端此时也没有数据要发送,一直处于 establish 状态,客户端恢复后,向服务端建立连接,此时服务端会怎么处理:

  • 客户端的 SYN 报文里的端口号与历史连接不相同:服务器重新建立新连接,原来的 TCP 连接:
    • 如果服务端发送了数据包给客户端,此时客户的内核就会回 RST 报文,服务端收到后就会释放连接
    • 如果服务端一直没有发送数据包给客户端,在超过一段时间后, TCP 保活机制就会启动
  • 客户端的 SYN 报文里的端口号与历史连接相同:由于序列号不同,服务器会回复一个携带了正确序列号和确认号的 ACK 报文,客户端收到该报文后,发现不是自己期待的,就会发回 RST 报文,此时,服务端释放该连接

可以使用命令 killcx <IP地址>:<端口号> 来关闭某个特定的 TCP 连接,其利用 Challenge ACK 中的有效信息,构造出两个 RST 报文,用于终止双方的 TCP 连接:

64008

25 序列化和反序列化

序列化和反序列化:在应用程序中,对象如果想要通过网络传输到另一端上或者保存在文件中,就需要将其序列化,而如果想要将序列化的对象读取,则需要反序列化

常见的序列化:

  • JDK 原生序列化:缺陷是不能跨语言
  • JSON:进行序列化的额外空间开销比较大
  • ProtoBuf:由 Google 出品的,是一种轻便、高效的结构化数据存储格式,可以用于结构化数据序列化,并且支持跨平台,使用前需要定义 IDL(Interface description language)文件

26 SYN 报文何时被丢弃

SYN 报文丢失的两种场景:

  • 开启 tcp_tw_recycle 参数,并且在 NAT 环境下,造成 SYN 报文被丢弃
  • accpet 队列满了,造成 SYN 报文被丢弃

对于主动关闭方,最后一个状态是 TIME_WAIT,其作用是:

  • 防止具有相同四元组的旧数据包被收到,也就是防止历史连接中的数据,被后面的连接接受
  • 保证被动关闭连接的一方能被正确的关闭,即保证最后的 ACK 能让被动关闭方接收

Linux 提供了两个参数快速回收处于 TIME_WAIT 的状态:

  • net.ipv4.tcp_tw_reuse:客户端在调用 connect() 函数时,内核会随机找一个 time_wait 状态超过 1 秒的连接给新的连接复用,所以该选项只适用于连接发起方
  • net.ipv4.tcp_tw_recycle:允许处于 TIME_WAIT 状态的连接被快速回收

对于服务器来说,如果同时开启了 recycle 和 timestamps 选项,则会开启 per-host 的 PAWS 机制。

  • PAWS 机制:开启了 tcp_timestamps 之后,该机制自动启动,它的作用是防止 TCP 包中的序列号发生绕回,因为 seq 位数 32,可能存在一次连接中,两次 seq 是相同值。PAWS 机制如果发现收到的数据包中时间戳不是递增的,则表示该数据包是过期的,就会直接丢弃这个数据包
  • per-host 的 PAWS 机制:per-host 是对对端 IP 做 PAWS 检查,而非对IP + 端口四元组做 PAWS 检查

SYN 包丢失场景一:当客户端 A 通过 NAT 网关和服务器建立 TCP 连接,然后服务器主动关闭并且快速回收 TIME-WAIT 状态的连接后,客户端 B 也通过 NAT 网关和服务器建立 TCP 连接,注意客户端 A 和 客户端 B 因为经过相同的 NAT 网关,所以是用相同的 IP 地址与服务端建立 TCP 连接,如果客户端 B 的 timestamp 比 客户端 A 的 timestamp 小,那么由于服务端的 per-host 的 PAWS 机制的作用,服务端就会丢弃客户端主机 B 发来的 SYN 包。

SYN 包丢失场景二:在服务端并发处理大量请求时,如果 TCP accpet 队列过小,或者应用程序调用 accept() 不及时,就会造成 accpet 队列满了 ,这时后续的连接就会被丢弃,这样就会出现服务端请求数量上不去的现象。

Socket 连接过程:

64010

27 TIME_WAIT 状态的作用

TIME_WAIT 状态的作用:

  • 防止历史连接中的数据,被后面相同四元组的连接错误的接收

    64011

  • 保证「被动关闭连接」的一方,能被正确的关闭

    64012

tcp_tw_reuse 参数用于客户端 在调用 connect() 函数时,内核会随机找一个 TIME_WAIT 状态超过 1 秒的连接给新的连接复用,所以该选项只适用于连接发起方。

为了使得上述参数起作用,需要开启 tcp_timestamps 参数,起作用有:

  • 便于精确计算 RTT
  • 能防止序列号回绕(PAWS)

为什么 tcp_tw_reuse 默认是关闭的:

  • 开启 tcp_tw_reuse 后,对于 RST 报文,即使其时间戳过期了,只要 RST 报文的序列号在对方的接收窗口内,也是能被接受的

    RFC:建议 RST 段不携带时间戳,并且无论其时间戳如何,RST 段都是可接受的。老的重复的 RST 段应该是极不可能的,并且它们的清除功能应优先于时间戳

    64013

  • 如果第四次挥手的 ACK 报文丢失了,有可能会导致被动关闭连接的一方不能被正常的关闭

    64014

tcp_tw_reuse 的作用是让客户端快速复用处于 TIME_WAIT 状态的端口,相当于跳过了 TIME_WAIT 状态,这可能会出现这样的两个问题:

  • 历史 RST 报文可能会终止后面相同四元组的连接,因为 PAWS 即使发现 RST 是过期的,也不会丢弃
  • 如果第四次挥手的 ACK 报文丢失了,有可能被动关闭连接的一方不能被正常的关闭

28 DNS 解析

客户端对本地 DNS 服务器采用递归查询,本地 DNS 服务器对 DNS 根服务器进行迭代查询。

IPv4 的根域名服务器一共有 13 个,其是由 UDP 包大小决定的,RFC 规定,DNS 报文要求被控制在 512 字节之内。

DNS 主根域名服务器在美国,如果美国终止 cn 后缀的解析,并不会导致中国国内的网络瘫痪,因为中国已经有很多台镜像的根域名服务器,可以让其继续支持 cn 后缀的解析,但是国外用户可能就无法访问 cn 后缀的网站了。

29 异常情况下的 TCP 连接

在 Linux 中,TCP 连接信息会放在一个 struct socket 结构体中,拔掉网线并不会影响 TCP 的连接状态。如果之后进行下面不同操作:

  • 拔掉网线,有数据传输:客户端会触发超时重传,如果一直没有将网线插回去,达到重传次数上限或者最大超时时间时,就会停止重传,内核断开 TCP 连接
  • 拔掉网线,无数据传输:如果没有开启 TCP keepalive 机制,该连接一直保存;如果开启了 TCP keepalive 机制,等待一段时间后,就会发送 TCP 保活报文到服务端,如果一直没有网络,最终达到最大保活探测次数后,TCP 会报告该连接已经死亡

客户端宕机:跟拔掉网线是一样无法被服务端的感知的,所以如果在没有数据传输,并且没有开启 TCP keepalive 机制时,服务端的 TCP 连接将会一直处于 ESTABLISHED 连接状态,直到服务端重启进程

客户端杀死进程:杀死客户端的进程后,客户端的内核就会向服务端发送 FIN 报文,与服务端四次挥手