什么是 TCP 窗口更新?
我正在为Java游戏制作自己的自定义服务器软件(游戏和原始服务器软件是用Java编写的)。没有任何可用的协议文档,所以我不得不用Wireshark读取数据包。
当客户端连接时,服务器以 Gzip 格式向其发送级别文件。在大约94个数据包进入发送级别时,我的服务器使用ArrayIndexOutOfBoundsException使客户端崩溃。根据来自原始服务器的捕获文件,它会在大约该点发送 TCP 窗口更新。什么是 TCP 窗口更新,如何使用套接字通道发送一个?
我正在为Java游戏制作自己的自定义服务器软件(游戏和原始服务器软件是用Java编写的)。没有任何可用的协议文档,所以我不得不用Wireshark读取数据包。
当客户端连接时,服务器以 Gzip 格式向其发送级别文件。在大约94个数据包进入发送级别时,我的服务器使用ArrayIndexOutOfBoundsException使客户端崩溃。根据来自原始服务器的捕获文件,它会在大约该点发送 TCP 窗口更新。什么是 TCP 窗口更新,如何使用套接字通道发送一个?
TCP 窗口用于连接上对等方之间的流控制。对于每个 ACK 数据包,主机将发送一个“窗口大小”字段。此字段表示主机在数据已满之前可以接收多少字节的数据。发送方发送的数据不应超过该数量的数据。
如果客户端接收数据的速度不够快,则窗口可能会变满。换句话说,TCP 缓冲区可能会在应用程序停止执行除从其套接字读取以外的其他操作时填满。发生这种情况时,客户端将发送一个设置了“窗口已满”位的 ACK 数据包。此时,服务器应该停止发送数据。发送到具有完整窗口的计算机的任何数据包都不会被确认。(这将导致行为不端的发件人重新传输。行为良好的发送方将仅缓冲传出数据。如果发送端的缓冲区也填满了,则发送应用程序在尝试将更多数据写入套接字时将阻止!
这是一个 TCP 停顿。发生这种情况的原因有很多,但最终它只是意味着发送方的传输速度比接收方读取的速度快。
一旦接收端的应用程序重新开始从套接字读取,它将耗尽一些缓冲数据,从而释放一些空间。然后,接收方将发送一个“窗口更新”数据包,以告诉发送方它可以传输多少数据。发送方开始传输其缓冲数据,流量应正常流动。
当然,如果接收器始终保持缓慢,您可能会反复失速。
我的措辞好像发送方和接收方是不同的,但实际上,两个对等方都在交换每个ACK数据包的窗口更新,并且任何一方都可以填满其窗口。
总体信息是,您不需要直接发送窗口更新数据包。实际上,欺骗一个实际上是一个坏主意。
关于您看到的异常...它不太可能是由窗口更新数据包引起或阻止的。但是,如果客户端读取速度不够快,则可能会丢失数据。在服务器中,应检查 Socket.write() 调用的返回值。它可能小于您尝试写入的字节数。如果发送方的传输缓冲区已满,则会发生这种情况,这可能会在 TCP 停顿期间发生。您可能会丢失字节。
例如,如果您尝试在每次写入调用时写入 8192 个字节,但其中一个调用返回 5691,则需要在下一次调用时发送剩余的 2501 个字节。否则,客户端将看不到该 8K 块的其余部分,并且您的文件在客户端将比在服务器端短。
这发生在TCP / IP堆栈的深处;在您的应用程序(服务器和客户端)中,您不必担心TCP窗口。错误必须是其他原因。