什么是TCP窗口更新?

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

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

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

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

这是一个TCP停顿。 它可能由于很多原因而发生,但最终它只是意味着发送者的传输速度比接收者正在读取的速度快。

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

当然,如果接收器一直很慢,你可以得到重复的停顿。

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

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

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

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

这在TCP / IP堆栈中非常深入; 在您的应用程序(服务器和客户端)中,您不必担心TCP窗口。 错误必须是别的。

TCP窗口更新与在发送方和接收方之间传递可用缓冲区大小有关。 ArrayIndexOutOfBoundsException不是可能的原因。 最有可能的是,代码期望某种类型的数据没有得到(很可能在此之前它只是现在引用)。 没有看到代码和堆栈跟踪,很难说更多。

TCP WindowUpdate – 这表示该段是纯WindowUpdate段。 当接收方的应用程序已消耗已从RX缓冲区接收的数据时,会发生WindowUpdate,导致TCP层向另一方发送WindowUpdate,以指示缓冲区中现在有更多可用空间。 通常在TCP ZeroWindow条件发生后看到。 一旦接收器上的应用程序从TCP缓冲区检索数据,从而释放空间,接收方应通过发送通告当前窗口大小的TCP WindowUpdate通知发送方TCP ZeroWindow条件不再存在。

https://wiki.wireshark.org/TCP_Analyze_Sequence_Numbers

您可以访问此网站http://www.tcpipguide.com/free/index.htm以获取有关TCP / IP的大量信息。

除了例外,你有没有得到任何细节 ?

它可能与TCP窗口更新数据包无关
(你有没有看到它重复多个实例?)

更有可能与处理接收数据的处理代码有关。

这通常只是一个触发器,而不是问题的原因。

例如,如果使用NIO选择器,则窗口更新可能会触发写入通道的唤醒。 这反过来会触发代码中的错误逻辑。

获取堆栈跟踪,它将显示根本原因。