一尘不染

什么是TCP窗口更新?

java

我正在为Java游戏制作自己的自定义服务器软件(游戏和原始服务器软件都是用Java编写的)。没有可用的协议文档,因此我必须使用Wireshark读取数据包。

客户端连接时,服务器将以Gzip格式将其发送到级别文件。在发送大约94个数据包时,我的服务器通过ArrayIndexOutOfBoundsException使客户端崩溃。根据原始服务器的捕获文件,它大约在该点发送一个TCP窗口更新。什么是TCP窗口更新,如何使用SocketChannel发送一个?


阅读 537

收藏
2020-12-03

共1个答案

一尘不染

TCP窗口用于连接上对等方之间的流控制。对于每个ACK数据包,主机将发送“窗口大小”字段。该字段表示主机在数据满之前可以接收多少字节的数据。发送方发送的数据量不应超过该数量。

如果客户端接收数据的速度不够快,则该窗口可能已满。换句话说,当应用程序关闭时,除了从套接字读取数据外,TCP缓冲区可能已满。发生这种情况时,客户端将发送一个“窗口已满”位设置的ACK数据包。此时,服务器应该停止发送数据。发送到具有完整窗口的计算机的任何数据包都
不会
被确认。(这将导致行为不端的发送方重新传输。行为良好的发送方将仅缓冲传出的数据。如果发送方的缓冲区也填满,则发送应用程序在尝试向套接字写入更多数据时将阻塞!)

这是一个TCP停顿。发生这种情况的原因有很多,但最终它仅意味着发送方的传输速度快于接收方的读取速度。

一旦接收端的应用程序恢复到从套接字读取数据,它将耗尽一些缓冲的数据,从而释放一些空间。然后,接收方将发送“窗口更新”数据包,以告诉发送方它可以传输多少数据。发送方开始传输其缓冲的数据,流量应正常流动。

当然,如果接收器持续缓慢,您可能会反复停顿。

我说的这句话好像发送方和接收方不同,但实际上,两个对等方都在与每个ACK数据包交换窗口更新,并且任何一方都可以填充其窗口。

总的信息是您不需要直接发送窗口更新数据包。欺骗一个人实际上不是一个好主意。

关于您看到的异常……它不太可能是由窗口更新数据包引起或阻止的。但是,如果客户端读取速度不够快,则可能会丢失数据。在服务器中,应检查Socket.write()调用的返回值。它可能少于您要写入的字节数。如果发送方的发送缓冲区已满,则会发生这种情况,这可能在TCP停顿期间发生。您可能正在丢失字节。

例如,如果您尝试在每次调用时写入8192字节,但是其中一个调用返回5691,则您需要在下一个调用中发送剩余的2501字节。否则,客户端将看不到该8K块的其余部分,并且您的文件在客户端比在服务器端短。

2020-12-03