TCP 窗口用于连接上的对等方之间的流量控制。对于每个 ACK 数据包,主机将发送一个“窗口大小”字段。该字段表示主机在满之前可以接收多少字节的数据。发件人不应发送超过该数量的数据。
如果客户端接收数据的速度不够快,窗口可能会变满。换句话说,当应用程序关闭而不是从其套接字读取时,TCP 缓冲区可能会被填满。发生这种情况时,客户端将发送一个设置了“窗口已满”位的 ACK 数据包。此时,服务器应该停止发送数据。任何发送到具有完整窗口的机器的数据包都不会被确认。(这将导致行为不良的发送方重新传输。行为良好的发送方只会缓冲传出数据。如果发送方的缓冲区也填满,则发送应用程序在尝试向套接字写入更多数据时将阻塞!)
这是一个 TCP 停顿。发生这种情况的原因有很多,但最终它只是意味着发送方的传输速度快于接收方的读取速度。
一旦接收端的应用程序重新开始从套接字读取,它将耗尽一些缓冲数据,从而释放一些空间。然后接收方将发送一个“窗口更新”数据包,告诉发送方它可以传输多少数据。发送方开始传输其缓冲数据并且流量应该正常流动。
当然,如果接收器一直很慢,您可能会遇到重复的停顿。
我的措辞好像发送者和接收者不同,但实际上,两个对等方都在与每个 ACK 数据包交换窗口更新,并且任何一方都可以填满它的窗口。
总体信息是您不需要直接发送窗口更新数据包。欺骗一个人实际上是一个坏主意。
关于您看到的异常......它不太可能是由窗口更新数据包引起或阻止的。但是,如果客户端读取速度不够快,您可能会丢失数据。在您的服务器中,您应该检查 Socket.write() 调用的返回值。它可能小于您尝试写入的字节数。如果发送方的传输缓冲区已满,就会发生这种情况,这可能发生在 TCP 停顿期间。您可能会丢失字节。
例如,如果您尝试在每次调用 write 时写入 8192 个字节,但其中一个调用返回 5691,那么您需要在下一次调用时发送剩余的 2501 个字节。否则,客户端将看不到该 8K 块的其余部分,并且您的文件在客户端将比在服务器端短。