(1)没有解包器,可能是这样的。 -------------------------------------- channelRead收到字节流:1024 channelRead收到字节流:1024 channelRead收到字节流:1024 channelRead收到字节流:1024 channelRead收到字节流:656 channelReadComplete....
channelRead收到字节流:2920 channelReadComplete....
channelRead收到字节流:4096 channelRead收到字节流:284 channelReadComplete.... -------------------------------- 从缓冲区,读取一些数据,就触发一次Read方法。 读光了,触发channelReadComplete方法。 即使,发送的是超级大包,也会不断的触发Read方法。
(2)有解包器,可能是这样的: ------------------------------------------ channelReadComplete.... channelReadComplete.... channelReadComplete.... channelReadComplete.... channelReadComplete.... channelReadComplete.... channelRead收到字节流:495837
channelReadComplete.... channelRead收到字节流:579 channelRead收到字节流:166 ------------------------------------------ 读光了,触发channelReadComplete方法。这个一样。 解出的每个消息都触发一次Read方法。 对于大包,需要channelReadComplete多次,才会收到一个消息,才触发一次Read方法。 对于小包,可能一次channelReadComplete,就包含了多个消息,触发多次Read方法。
(3)前提,有解包器。开始用的netty-all-4.0.27.Final, ReadTimeoutHandler类里:channelRead方法里会更新lastReadTime。 当传输大包时,可能需要几分钟才收完一个大包,才会触发Read方法。 最终导致触发Read超时事件。 从netty-all-4.0.29.Final版本开始,netty改成了channelReadComplete方法里更新lastReadTime,就没有问题了。 注意: netty-all-4.1.36.Final.jar里,又不一样。 channelReadComplete里只有第一次read时才更新时间
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { if((readerIdleTimeNanos > 0L || allIdleTimeNanos > 0L) && reading) { lastReadTime = ticksInNanos(); reading = false; } ctx.fireChannelReadComplete(); }
具体哪个版本开始变成这样的,没有去追究。
(4)别人的解释: 当channel上面有数据到来时会触发channelRead事件,当数据到来时,eventLoop被唤醒继而调用channelRead方法处理数据。
channelReadComplete事件参考 How does netty determine when a read is complete? 大致意思是eventLoop被到来的数据唤醒后read数据并包装成msg,然后将msg作为参数调用channelRead方法,期间做个判断,read到0个字节或者是read到的字节数小于buffer的容量,满足以上条件就会调用channelReadComplete方法 ================================================== 补记: 其实,上述打包超时的原因,是因为ReadTimeoutHandler放在了拆包器后面,如果放在前面就没有问题了。 (1)在AbstractNioByteChannel的read()方法中 ------------------------------------ do { ... pipeline.fireChannelRead(byteBuf); .. } while (++ messages < maxMessagesPerRead);
pipeline.fireChannelReadComplete(); 比如每次读1024字节,最后触发complete,这个跟现象是一致的。 没有拆包器,就是先read,后complete。 有拆包器,大包是先complete,后read,小包反之。
(2)拆包器都是ByteToMessageDecoder的子类,在它的channelRead方法里, 会调用decode方法转换消息,然后对结果 for(int i = 0; i < size; ++i) { ctx.fireChannelRead(out.get(i)); } 遍历触发read方法。
|