【问题标题】:When read() rates is slower than arrived TCP packet rate当 read() 速率低于到达 TCP 数据包速率时
【发布时间】:2020-01-01 09:18:46
【问题描述】:

我有一个关于 TCP 行为的问题。

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

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


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

我从发送者主机生成了很多 TCP 数据包。

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

我把这个缓冲值变小了。

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

那么,

我发现 [PSH-ACK TCP packet] 重传发生在接收主机。

因此,我决定慢速read()可以重传。但我不确定。

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

【问题讨论】:

  • 它本身不会导致重传。最终,发件人将停滞不前。

标签: sockets tcp system-calls


【解决方案1】:

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 有一个零窗口并且一个段到达它时 仍然必须发送一个确认,显示它的下一个预期序列 编号和当前窗口(零)。

【讨论】:

  • 非常感谢您的评论!但我有一个问题。在接收端,它的滑动窗口在向发送端发送 ACK 数据包时,或者在读取接收套接字缓冲区时,它的滑动窗口正在向前移动?
  • 我猜如果满足两个条件,(1) 向发送方发送 ACK 数据包,(2) 读取它的套接字缓冲区,滑动窗口将向前移动以接收最新的数据包。对吗?
  • @nimdrak 在答案中添加了一个 tcpdump 捕获示例。也许这会有所帮助。
  • 我真的很感谢你。由于没有调用recv(),窗口大小似乎在不断减小,对吧?我应该像你一样做一个小测试。谢谢你的课。
猜你喜欢
  • 1970-01-01
  • 2021-12-07
  • 2010-12-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-12
相关资源
最近更新 更多