English 中文(简体)
Java NIO:快速发送大型消息会导致截断数据包和数据丢失。
原标题:
  • 时间:2009-01-31 16:07:42
  •  标签:

我有一个麻烦问题,就是从一个运行在Linux上的Java(NIO)服务器快速连续地向客户端发送多个大型消息会导致数据包被截断。必须发送大型消息并且速度非常快才会有这个问题发生。这里基本上是我的代码在做什么(不是实际代码,但大致描述了正在发生的情况):

//-- 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+  ));
  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的消息时强制发送任何缓冲区中的内容(在我的应用程序中并不经常发生)。但我不知道是否有一种方法来强制发送缓冲区(或等待它发送完)。谢谢任何帮助!

最佳回答

sc.write() 无法发送全部或部分数据的原因很多。您必须检查返回值和/或缓冲区中剩余的字节数。

for (int n=0; n<20; n++){
  ByteBuffer bb = encoder.encode(CharBuffer.wrap(msg+  ));
  if(sc.write(bb) > 0 && bb.remaining() == 0) {
    // all data sent
  } else {
    // could not send all data.
  }
  bb.rewind();
}
问题回答

你没有检查以下操作的返回值:

sc.write(bb);

这将返回已写入的字节数,可能少于您缓冲区中可用的数据。由于nio的工作方式,您可以只需调用bytebuffer的remaining()方法来查看是否有剩余数据。

I haven t done any NIO programming, but according to the Javadocs, sc.write() will not write the entire ByteBuffer if the SocketChannel is in non-blocking mode (as yours is) and the socket s output buffer is full.

Because you are writing so quickly, it is very likely that you are flooding your connection and your network or receiver cannot keep up.

I d be happy if I could programmatically check if it is not ready for more data yet

You need to check the return value of sc.write() to find out whether your output buffer is full.

Don t assume you have any control over what data ends up in which packet.

More here: What s the best way to monitor a socket for new data and then process that data?

You are using the non-blocking mode: sc.configureBlocking(false);

Set blocking to true and your code should work as it is. Suggestions made by others here to check send count and loop will also work.





相关问题
热门标签