我有一个令人讨厌的问题,即从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的消息时,明确强制完全发送任何缓冲的内容(这在我的应用程序中并不常见).但我不知道有什么方法可以强制发送缓冲区(或者等到它发送完毕).谢谢你的帮助!
sc.write()不会发送部分或全部数据的原因有很多.您必须检查缓冲区中的返回值和/或剩余字节数.
for (int n=0; n<20; n++){ ByteBuffer bb = encoder.encode(CharBuffer.wrap(msg+'\0')); 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()以查看是否有任何剩余.