19

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

当客户端连接服务器时,它会以 Gzip 格式发送关卡文件。在发送关卡大约 94 个数据包时,我的服务器使用 ArrayIndexOutOfBoundsException 使客户端崩溃。根据来自原始服务器的捕获文件,它大约在那个时候发送一个 TCP 窗口更新。什么是 TCP 窗口更新,我将如何使用 SocketChannel 发送一个?

4

7 回答 7

64

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

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

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

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

当然,如果接收器一直很慢,您可能会遇到重复的停顿。

我的措辞好像发送者和接收者不同,但实际上,两个对等方都在与每个 ACK​​ 数据包交换窗口更新,并且任何一方都可以填满它的窗口。

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

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

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

于 2009-09-23T14:56:54.090 回答
6

这发生在 TCP/IP 堆栈的深处。在您的应用程序(服务器和客户端)中,您不必担心 TCP 窗口。错误一定是别的东西。

于 2009-09-23T14:29:17.007 回答
3

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

https://wiki.wireshark.org/TCP_Analyze_Sequence_Numbers

于 2015-09-07T13:01:54.350 回答
2

TCP 窗口更新与发送方和接收方之间的可用缓冲区大小通信有关。ArrayIndexOutOfBoundsException 不是可能的原因。最有可能的是代码正在期待某种它没有得到的数据(很可能早在它现在才引用的这一点之前)。没有看到代码和堆栈跟踪,真的很难说更多。

于 2009-09-23T14:34:35.167 回答
0

您可以进入这个网站http://www.tcpipguide.com/free/index.htm以获取有关 TCP/IP 的大量信息。

于 2009-09-23T14:33:58.583 回答
0

除了例外,您有任何详细信息吗?

它不太可能与 TCP 窗口更新数据包有关
(您是否看到它在多个实例中完全重复?)

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

于 2009-09-23T14:39:19.893 回答
0

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

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

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

于 2009-09-23T14:40:58.553 回答