TCP 协议本身有几种不同的机制来处理瓶颈。
例如(谷歌详情):
简而言之:TCP 堆栈使用窗口来避免向对等方发送的数据多于它可以存储到接收队列的数据。
当发送方尝试发送的速度超过网络/接收方可以处理的速度时,
发送方的 TCP 堆栈的输出队列将被填满。
当队列已满时,发送者会通过以下几种方式注意到这一点:
- 带有阻塞套接字:
send() 调用将阻塞。
- 使用解锁套接字:
send() 调用将失败,并出现 errno EAGAIN 或 EWOULDBLOCK。
-
select() 调用:writefd-set 不会指示写入可能性。
这样 TCP 堆栈可以自动减慢发送方的速度,从而最大限度地减少网络/接收端的数据包丢失。
编辑:
Tcpdump 示例,其中服务器 (B) 在 accept() 之后不调用 recv():
~ # tcpdump -i eth0 "tcp port 12345"
21:44:16.255183 A > B: seq 2052761822, win 64240, options [mss 1460,nop,wscale 8,nop,nop,sackOK], length 0
21:44:16.255484 B > A: seq 2110966471, ack 2052761823, win 7300, options [mss 1460,nop,nop,sackOK], length 0
21:44:16.256065 A > B: ack 1, win 64240, length 0
21:44:20.338089 A > B: seq 1:1461, ack 1, win 64240, length 1460
21:44:20.338365 B > A: ack 1461, win 5840, length 0
21:44:20.338754 A > B: seq 1461:2921, ack 1, win 64240, length 1460
21:44:20.338978 B > A: ack 2921, win 5840, length 0
21:44:20.339357 A > B: seq 2921:4381, ack 1, win 64240, length 1460
21:44:20.339759 A > B: seq 4381:5841, ack 1, win 64240, length 1460
21:44:20.340175 A > B: seq 5841:7301, ack 1, win 64240, length 1460
21:44:20.340571 A > B: seq 7301:8761, ack 1, win 64240, length 1460
21:44:20.373395 B > A: ack 8761, win 1460, length 0
21:44:20.374367 A > B: seq 8761:10221, ack 1, win 64240, length 1460
21:44:20.413398 B > A: ack 10221, win 0, length 0
21:44:20.714460 A > B: seq 10221:10222, ack 1, win 64240, length 1
21:44:20.714725 B > A: ack 10221, win 0, length 0
21:44:21.314796 A > B: seq 10221:10222, ack 1, win 64240, length 1
21:44:21.315055 B > A: ack 10221, win 0, length 0
21:44:22.515652 A > B: seq 10221:10222, ack 1, win 64240, length 1
21:44:22.515925 B > A: ack 10221, win 0, length 0
21:44:24.917211 A > B: seq 10221:10222, ack 1, win 64240, length 1
21:44:24.917473 B > A: ack 10221, win 0, length 0
21:44:29.718352 A > B: seq 10221:10222, ack 1, win 64240, length 1
21:44:29.718612 B > A: ack 10221, win 0, length 0
21:44:39.331520 A > B: seq 10221:10222, ack 1, win 64240, length 1
21:44:39.331789 B > A: ack 10221, win 0, length 0
客户端 (A) 尝试向服务器 (B) 发送大段,直到服务器开始通告窗口大小 0。在此之后客户端 (A) 开始使用段大小 1 字节,并且重新传输之间的时间间隔开始增加.看起来,TCP 堆栈试图最小化轮询窗口所需的流量。
RFC-793 说明如下(稍后的 RFC 可能会更好地说明这一点):
发送 TCP 必须准备好接受来自用户并发送到
即使发送窗口为零,也至少有一个八位字节的新数据。
发送 TCP 必须定期重新传输到接收 TCP,即使在
窗口为零。建议重传两分钟
窗口为零时的间隔。这种重传是必不可少的
保证当任一 TCP 具有零窗口时,重新打开
窗口将可靠地报告给其他人。
当接收 TCP 有一个零窗口并且一个段到达它时
仍然必须发送一个确认,显示它的下一个预期序列
编号和当前窗口(零)。