当 read() 速率低于到达的 TCP 数据包速率时

When read() rates is slower than arrived TCP packet rate

我对 TCP 行为有疑问。

在接收方, 当系统调用 read() 比 TCP 数据包到达率慢时, 是否出现丢包重传?

我知道接收套接字缓冲区在接收到超过其大小的数据包时会溢出。但是这个和上面的不一样。


为了找到答案,我做了一个小实验。

我从发送主机生成了大量 TCP 数据包。

接收方主机从其套接字缓冲区中读取数据包。

我把这个缓冲值调小了很多。

(ex. read(socket, buf, small_size) // small_size 是 1kbyte

然后,

我发现 [PSH-ACK TCP 数据包] 重传发生在接收方主机上。

所以我决定慢read()可以重发。但我不确定。

你能给我一点提示吗?非常感谢!

TCP 协议本身有几种不同的瓶颈处理机制。 例如(google 详情):

  • 慢启动
  • 流量控制
  • 拥塞控制

简而言之:TCP 堆栈使用 windows 来避免向对等方发送的数据超过它可以存储到接收队列的数据。

当发件人尝试发送的速度超过 network/receiver 可以处理的速度时, 发送方的 TCP 堆栈的输出队列将被填满。 当队列已满时,发件人会通过几种方式注意到这一点:

  • 带阻塞套接字:send() 调用将阻塞。
  • 使用解锁套接字:send() 调用将失败,错误号为 EAGAIN 或 EWOULDBLOCK。
  • whith select() 调用:writefd-set 不会指示写入可能性。

这样 TCP 堆栈可以自动减慢发送方的速度,从而最大限度地减少 network/receiving 端丢失的数据包。

编辑:

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) 发送大段,直到服务器开始通告 window 大小 0。此后,客户端 (A) 开始使用大小为 1 字节的段,并使用句点重传之间开始增加。看起来,TCP 堆栈试图最小化轮询 window.

所需的流量

RFC-793 说明如下(以后的 RFC 可能会更好地说明这一点):

The sending TCP must be prepared to accept from the user and send at least one octet of new data even if the send window is zero. The
sending TCP must regularly retransmit to the receiving TCP even when
the window is zero. Two minutes is recommended for the retransmission interval when the window is zero. This retransmission is essential to guarantee that when either TCP has a zero window the re-opening of the window will be reliably reported to the other.

When the receiving TCP has a zero window and a segment arrives it must still send an acknowledgment showing its next expected sequence number and current window (zero).