1. 三次握手
- Client -> Server:
SYN x = rand() - Client <- Server:
ACK x+1, y = rand() - Client -> Server:
ACK x+1, y+1
注意:只有在一次完整的 roundtrip 之后 Client 才能开始发送数据;Server 则需要收到 ACK x+1, y+1 消息到达后才能开始发送数据。
** TCP Fast Open **
- 打开此配置时,可以第一条
SYN消息里带上数据,不用等一次完整的 roundtrip - 限制:
SYN消息中可带的数据大小有限制- 只有特定的几种 HTTP 请求可使用此方式发送
- 只能用于重复连接之前曾经连过的 Server 的情况下
- 配置:
- Server: Linux kernel v4.1+
- Client: iOS 9+/OSX 10.11+ 或他相应的 Linux 版本
- 应用程序打开相关的 socket flag
- 性能提升:
- HTTP网络延迟降低 15%
- 页面加载时间平均降低 10%
- 某些高延迟情况下有 40% 的降低
2. 拥塞控制
1) 限流(Flow control)
- 目的:防止一端发送过多数据使另一端来不及处理
- 机制:
- 客户端、服务端分别告知对方已方接收窗口的大小(
rwnd) - 连接刚建立时,两端的
rwnd都使用系统默认值 - 每个
ACK消息都会带上当前最新的rwnd,使对方可以动态调整发送的数据流量
- 客户端、服务端分别告知对方已方接收窗口的大小(
Window Scaling (RFC 1323)
- 目的:TCP协议只为
rwnd预留了16位,使rwnd最大为2^16=65536字节,要想设置超过此值的的窗口大小,需要 Window Scaling- 实现:在三次握手过程中确定一个左移位数(
shift),rwnd的实际大小为ACK消息中带的16位的值左移shift以后得到的值
- 支持 Window Scaling 以后,
rwnd最大支持1G字节- 主流系统上已默认开启
- 通信信道上的中间结点、路由器、防火墙等可能会禁用此选项
$> sysctl net.ipv4.tcp_window_scaling $> sysctl -w net.ipv4.tcp_window_scaling=1
2) 拥塞控制(Congestion control)
-
目的:防止发送太多数据导致网络来不及处理导致网络拥塞
-
机制:
- 慢启动(Slow-Start)
-
发送方为每个 tcp 连接维护一个拥塞窗口大小(
cwnd),其默认的初始值比较小。cwnd不需要通知对端- 为提高传输效率,可将
cwnd默认初始值提高到10个 network segments
-
引入规则:传输中未被
ACK的数据量限额为MIN(cwnd, rwnd) -
开始时
cwnd较小,随后每收到一个ACK,cwnd += 1,这样下次就可以发送之前两倍的数据量
Slow-Start Restart
- 连接空闲一段时间后会将
cwnd重设为默认值 - 为防止此机制影响连接的传输性能,可采用下列命令关闭此机制:
$> sysctl net.ipv4.tcp_slow_start_after_idle $> sysctl -w net.ipv4.tcp_slow_start_after_idle=0 -
- 拥塞避免(Congestion avoidance)
-
网络条件好的情况下,cwnd会一直倍增,直到其超出接收方系统配置的拥塞阈值窗口大小(ssthres),或出现丢包。此时会触发拥塞避免机制来调整拥塞窗口(cwnd)大小
-
常见算法:
-
TCP Tahoe and Reno
-
TCP Vegas
-
TCP New Reno
-
TCP BIC
-
TCP CUBIC (Linux上默认)
-
Compound TCP (Windows上默认)
-
Proportional Rate Reduction for TCP(RFC 6937)
- 目的:加快丢包后的恢复速度
- 性能:在有丢包的连接上平均延时降低3-10%
- Linux 3.2+ 内核上默认使用此拥塞避免算法
-
- 慢启动(Slow-Start)
3. 队头阻塞
TCP协议下每个数据包都有唯一的序号,协议能够保证这些包的可靠、按顺序传输。但如果多个包中某个序号比较靠前的在传输过程中发生了丢包,其它序号靠后的包即使已顺利到达,也需要在接收方的缓冲区中等待,直到队头包重传成功才能将报文组装并返回给应用层。
- 队头阻塞发生在
TCP协议层,应用层无法感知,应用层只能感知到总体的传输延迟 - 队头阻塞的负面影响在音、视频应用这种不要求所有包都到达即可正常运行的场景下尤其明显。
4. 四次挥手
- Client -> Server:
SEQ=x, FIN=1//Client=FIN_WAIT1 - Client <- Server:
SEQ=y, ACK=x+1//Client=FIN_WAIT2, Server=CLOSE_WAIT - Client <- Server:
可能有 Server 还未发完的数据传送继续进行 - ...
- Client <- Server:
SEQ=z, ACK=x+1, FIN=1//Server=LAST_ACK - Client -> Server:
SEQ=x+1, ACK=z+1//Client=TIME_WAIT(2MSL后自动进入CLOSED状态)