Netty3.10.1:关于MessageReceived

要分析粘包问题,首先要从数据读开始分析。那么,netty从哪开始读的呢?

-----------------------------------------------------------------------------

messageReceived
java.lang.Exception
 at org.jboss.netty.channel.SimpleChannelHandler.stack(SimpleChannelHandler.java:331)
 at org.jboss.netty.example.echo.EchoServerHandler.messageReceived(EchoServerHandler.java:47)
 at org.jboss.netty.channel.SimpleChannelHandler.handleUpstream(SimpleChannelHandler.java:95)
 at org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564)
 at org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:559)
 at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:268)
 at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:255)
 at org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:97)
 at org.jboss.netty.channel.socket.nio.AbstractNioWorker.process(AbstractNioWorker.java:108)
 at org.jboss.netty.channel.socket.nio.AbstractNioSelector.run(AbstractNioSelector.java:337)
 at org.jboss.netty.channel.socket.nio.AbstractNioWorker.run(AbstractNioWorker.java:89)
 at org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:187)
 at org.jboss.netty.util.ThreadRenamingRunnable.run(ThreadRenamingRunnable.java:108)
 at org.jboss.netty.util.internal.DeadLockProofWorker$1.run(DeadLockProofWorker.java:42)
 at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
 at java.lang.Thread.run(Thread.java:662)

以上是Netty读取消息以及触发我们的MessageReceived处理逻辑的整个线程栈。

下面就让我们从NioWorker.read开始分析

=============================================================

什么时候执行read操作?

代码如下:

 

 for (Iterator<SelectionKey> i = selectedKeys.iterator(); i.hasNext();) {

            SelectionKey k = i.next();

            i.remove();

            try {

                int readyOps = k.readyOps();

                if ((readyOps & SelectionKey.OP_READ) != 0 || readyOps == 0) {

                    if (!read(k)) {

                        // Connection already closed - no need to handle write.

                        continue;

                    }

                }

                if ((readyOps & SelectionKey.OP_WRITE) != 0) {

                    writeFromSelectorLoop(k);

                }

            } catch (CancelledKeyException e) {

                close(k);

            }

 

            if (cleanUpCancelledKeys()) {

                break// break the loop to avoid ConcurrentModificationException

            }

        }

其实也就是说,当对某个key进行OP_READ检测后,如果确实发生了数据可读事件,就执行读操作。

读操作,又做了哪些事情?

============================================================

代码如下:

 

@Override

    protected boolean read(SelectionKey k) {

     /*

     if(1==1){

     try {

throw new Throwable();

} catch (Throwable e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

     }*/

        final SocketChannel ch = (SocketChannel) k.channel();//获取对应的channel

        final NioSocketChannel channel = (NioSocketChannel) k.attachment();//获取对应的NioSocketChannel 

 

        final ReceiveBufferSizePredictor predictor =

            channel.getConfig().getReceiveBufferSizePredictor();

        final int predictedRecvBufSize = predictor.nextReceiveBufferSize();

        final ChannelBufferFactory bufferFactory = channel.getConfig().getBufferFactory();

 

        int ret = 0;

        int readBytes = 0;

        boolean failure = true;

 

        ByteBuffer bb = recvBufferPool.get(predictedRecvBufSize).order(bufferFactory.getDefaultOrder());

        //获取临时缓冲区

        try {

            while ((ret = ch.read(bb)) > 0) {//尽量往里面读数据

                readBytes += ret;

                if (!bb.hasRemaining()) {

                    break;

                }

            }

            failure = false;

        } catch (ClosedChannelException e) {

            // Can happen, and does not need a user attention.

        } catch (Throwable t) {

            fireExceptionCaught(channel, t);

        }

 

        if (readBytes > 0) {//如果读到了数据

            bb.flip();//转化成读模式

 

            final ChannelBuffer buffer = bufferFactory.getBuffer(readBytes);

            buffer.setBytes(0, bb);//这里是关键的复制操作。

            buffer.writerIndex(readBytes);//org.jboss.netty.buffer.BigEndianHeapChannelBuffer

 

            // Update the predictor.

            predictor.previousReceiveBufferSize(readBytes);

 

            // Fire the event.

            fireMessageReceived(channel, buffer);//收到消息了,就触发消息处理逻辑

        }

 

        if (ret < 0 || failure) {

            k.cancel(); // Some JDK implementations run into an infinite loop without this.

            close(channel, succeededFuture(channel));

            return false;

        }

 

        return true;

    }

这里的bufferFactory

org.jboss.netty.buffer.HeapChannelBufferFactory


buffer对应的类是

org.jboss.netty.buffer.BigEndianHeapChannelBuffer

好,不管怎么样,现在获取了消息,怎么触发消息处理机制呢?

---------------------------------------------------代码如下:

 

public static void fireMessageReceived(Channel channel, Object message, SocketAddress remoteAddress) {

        channel.getPipeline().sendUpstream(

                new UpstreamMessageEvent(channel, message, remoteAddress));

    }

所以,这里,其实交给对应的channel的处理管道pipiline来处理,这里的管道可能有好几个对象,这个由addLast函数添加。

注意2点:1 sendUpstream,这是处理的方向 2 UpstreamMessageEvent这是一个上行事件,只有有能力处理的ctx里的handler才可以处理。

------------------假如我们在自定义逻辑里加入了一个继承了SimpleChannelHandler的类或者SimpleChannelUpstreamHandler的类的对象(通过addLast函数)。

那么,消息就会最终传递到我们这个函数里。

----------------------------------------而这个函数,就是大部分时候,我们自己要增加的逻辑。

其实最基本的问题就是如何获取本次的内容。

 

 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {

      try{

          BigEndianHeapChannelBuffer behcb= (BigEndianHeapChannelBuffer)e.getMessage();

          System.out.println("\n\n\nreceived: "+new String(behcb.array(),"UTF-8"));

     }catch(Exception e11){

     }

}

其实根本原因在于,Netty刚开始会把socket里的数据读到一个ByteBuffer直接分配的缓冲区里,比如说1024个字节的容量,

最后读了256个字节,然后这256个数据会复制到BigEndianHeapChannelBuffer 的一个256长度的字节数组里。

所以我们此时通过这个256字节数组,获得了本次所读的256个字节的值,问题就解决了。

 

 

 

 

 

转载于:https://my.oschina.net/qiangzigege/blog/392864

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值