我遇到了这个令人讨厌的问题,即从 Java (NIO) 服务器(运行 Linux)快速连续地向客户端发送多条大消息会导致数据包被截断。消息必须很大并且发送速度非常快,问题才会发生。这基本上是我的代码在做什么(不是实际的代码,而是或多或少发生了什么):
//-- setup stuff: --
Charset charset = Charset.forName("UTF-8");
CharsetEncoder encoder = charset.newEncoder();
String msg = "A very long message (let's say 20KB)...";
//-- inside loop to handle incoming connections: --
ServerSocketChannel ssc = (ServerSocketChannel)key.channel();
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
sc.socket().setTcpNoDelay(true);
sc.socket().setSendBufferSize(1024*1024);
//-- later, actual sending of messages: --
for (int n=0; n<20; n++){
ByteBuffer bb = encoder.encode(CharBuffer.wrap(msg+'\0'));
sc.write(bb);
bb.rewind();
}
因此,如果数据包足够长并且尽可能快地发送(即在这样的循环中没有延迟),那么在另一端它通常会出现这样的情况:
[COMPLETE PACKET 1]
[COMPLETE PACKET 2]
[COMPLETE PACKET 3]
[START OF PACKET 4][SOME OR ALL OF PACKET 5]
存在数据丢失,数据包开始一起运行,因此数据包 5 的开头(在此示例中)与数据包 4 的开头到达相同的消息。它不仅仅是截断,而是一起运行消息。
我想这与 TCP 缓冲区或“窗口大小”有关,或者这里的服务器只是提供比操作系统或网络适配器或其他东西更快的数据可以处理它。但是我该如何检查并防止它发生呢?如果我减少每次使用 sc.write() 的消息长度,然后增加重复次数,我仍然会遇到同样的问题。这似乎只是短时间内数据量的问题。我也没有看到 sc.write() 抛出任何异常(我知道在上面的示例中我没有检查,但在我的测试中有)。
如果我能以编程方式检查它是否还没有准备好接收更多数据,我会很高兴,并延迟,等待它准备好。我也不确定是否“sc.socket().setSendBufferSize(1024*1024);” 有任何影响,或者如果我需要在 Linux 方面进行调整。有没有办法真正“冲洗”出 SocketChannel?作为一种蹩脚的解决方法,例如,我可以尝试在任何时候尝试发送超过 10KB 的消息时显式强制发送缓冲的任何内容(这在我的应用程序中并不常见)。但我不知道有什么方法可以强制发送缓冲区(或等到它发送)。谢谢你的帮助!