java netty之一个write跟踪(数据发送)
来源:互联网 发布:弯曲法测杨氏模量数据 编辑:程序博客网 时间:2024/06/04 18:07
在深入分析ServerBootstrap之前,先来跟踪一个write的过程吧,也就是数据发送的过程。。。。
先来看看这个例子的代码吧,server部分的初始化:
public class NettyServer {public void run() throws Exception {EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup); //前者用来处理accept事件,后者用于处理已经建立的连接的iob.channel(NioServerSocketChannel.class); //用它来建立新accept的连接,用于构造serversocketchannel的工厂类b.childHandler(new ChannelInitializer<SocketChannel>(){ //为accept的pipeline预添加的inboundhandler@Override //当新连接accept的时候,这个方法会调用protected void initChannel(SocketChannel ch) throws Exception {// TODO Auto-generated method stubch.pipeline().addLast(new MyChannelHandler());}});ChannelFuture f = b.bind(80).sync(); //在所有的网卡上监听这个端口f.channel().closeFuture().sync();} finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}public static void main(String args[]) throws Exception {new NettyServer().run();}}接下来是handler部分的代码:
public class MyChannelHandler extends ChannelInboundByteHandlerAdapter {@Overrideprotected void inboundBufferUpdated(ChannelHandlerContext ctx, ByteBuf in)throws Exception {// TODO Auto-generated method stub//while (in.isReadable()) {//System.out.print((char)in.readByte());//}ByteBuf b = ctx.alloc().buffer();b.writeBytes("aaa".getBytes());ctx.pipeline().write(b);ctx.pipeline().flush().addListener(new ChannelFutureListener(){@Overridepublic void operationComplete(ChannelFuture future)throws Exception {// TODO Auto-generated method stubfuture.channel().close().sync();}});}}这个要实现的功能还是很简单的,当建立了连接并收到数据之后,向客户端发送aaa。。。好了,接下来我们来跟踪一下整个过程。。。。
代码首先创建了一个ByteBuf,然后再将aaa写入到buffer里面,然后再发送buf,那是因为NioSocketChannel是基于byte的,因此它的unsafe对象也是基于byte的。。。。
先看看pipeline的write方法:(defaultchannelpipeline)
//从尾handler的context的write方法 public ChannelFuture write(Object message) { return tail.write(message); }这里调用的是tailhander的context的write方法,而tailhandler是一个inboundhandler,因此会向前寻找到第一个oubounhander来处理,在前面的文章也有提到过对于读取的数据,pipeline上面的处理流程是从head到tail,而对于发送出去的数据,则是从tail到head。
好了,接下来看context的write方法:(defaultchannelhandlercontext)
//调用write方法,并将创建的promise返回 public ChannelFuture write(Object message) { return write(message, newPromise()); }额,接着再来看这里调用的write方法吧:
@Override public ChannelFuture write(final Object message, final ChannelPromise promise) { if (message instanceof FileRegion) { //如果是fileregion的话, return sendFile((FileRegion) message, promise); } if (message == null) { throw new NullPointerException("message"); } validateFuture(promise); DefaultChannelHandlerContext ctx = prev; EventExecutor executor; final boolean msgBuf;//向上寻找到第一个有buf的outboundhandler if (message instanceof ByteBuf) { //如果是bytebuf的类型,那么向上找到第一个有outbytebuffer的outboundhandler for (;;) { if (ctx.hasOutboundByteBuffer()) { msgBuf = false; //表示不是msg,是byte executor = ctx.executor(); break; }//message的类型,基于byte的handler和基于message的handler都行,因为最终到了head都会处理,成为基于byte的 if (ctx.hasOutboundMessageBuffer()) { msgBuf = true; // executor = ctx.executor(); break; } ctx = ctx.prev; } } else { msgBuf = true; for (;;) { if (ctx.hasOutboundMessageBuffer()) { executor = ctx.executor(); break; } ctx = ctx.prev; } }//调用刚刚找到的那个有outbuf的handler的context的write0方法 if (executor.inEventLoop()) { ctx.write0(message, promise, msgBuf); return promise; } final DefaultChannelHandlerContext ctx0 = ctx; executor.execute(new Runnable() { @Override public void run() { ctx0.write0(message, promise, msgBuf); } }); return promise; }这部分代码就能很明显的看出是从tail向head方向寻找的吧。。。由于我们前期并没有定义outboundhandler,所以这里找到的将是pipeline上面默认的headhandler,好了,接下来来看write0方法吧:
//将数据写入到outbuf中,并调用flush方法 private void write0(Object message, ChannelPromise promise, boolean msgBuf) { Channel channel = channel(); //如果当前的channel并没有 if (!channel.isRegistered() && !channel.isActive()) { promise.setFailure(new ClosedChannelException()); return; } if (isOutboundFreed()) { promise.setFailure(new ChannelPipelineException( "Unable to write as outbound buffer of next handler was freed already")); return; } if (msgBuf) { //加入message outboundMessageBuffer().add(message); } else { ByteBuf buf = (ByteBuf) message; try { //将要发送的数据写进outhandler outboundByteBuffer().writeBytes(buf, buf.readerIndex(), buf.readableBytes()); } finally { buf.release(); } } //flush操作,将缓冲区的数据发送出去 invokeFlush0(promise); }将数据写入到outbuf中去,然后最后调用invokeFlush0,好了这里来看看这个invokeFlush0f方法吧:
//将buf里面的数据flush出去 private void invokeFlush0(ChannelPromise promise) { if (isOutboundFreed()) { promise.setFailure(new ChannelPipelineException( "Unable to flush as outbound buffer of next handler was freed already")); return; } Channel channel = channel(); //获取channel if (!channel.isActive() && !channel.isRegistered()) { promise.setFailure(new ClosedChannelException()); return; }//提取当前的context的handler,因为是outboundhandler,所以也是operationhandler ChannelOperationHandler handler = (ChannelOperationHandler) handler(); if (handler instanceof ChannelOutboundHandler) { //将bridge部分的数据flush,留在以后看 flushOutboundBridge(); } try { //真实的调用poutboundhandler或者说operationhandler的flush方法,用于将数据发送出去 //其实是headhandler的话,其实就是调用其unsafe对象的flush方法,说白了也就是所属channel的unsafe对象的flush方法 handler.flush(this, promise); } catch (Throwable t) { notifyHandlerException(t); } finally { if (handler instanceof ChannelOutboundByteHandler && !isOutboundFreed()) { try { ((ChannelOutboundByteHandler) handler).discardOutboundReadBytes(this); } catch (Throwable t) { notifyHandlerException(t); } } freeHandlerBuffersAfterRemoval(); } }代码还是很简单吧,说白了就是调用当前context的handler的flush方法,我们这里由于并没有定义自己的outboundhandler,所以这地调用的pipeline默认的headhandler的flush方法来将buf中的数据真正的发送出去。。
好了,那么接下来我们来看看headhandler的flush方法,由于NioSocketChannel是基于byte的,所以我们也看基于byte的headhandler的flush方法吧:
@Override public void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { int discardedMessages = 0; MessageBuf<Object> in = msgSink; for (;;) { //这里也是存在一定的可能性将要发送的数据写到了msgbuf中去 Object m = in.poll(); if (m == null) { break; } if (m instanceof ByteBuf) { ByteBuf src = (ByteBuf) m; //将数据提取出来,并写到bytebuf中去 byteSink.writeBytes(src, src.readerIndex(), src.readableBytes()); } else { logger.debug( "Discarded outbound message {} that reached at the head of the pipeline. " + "Please check your pipeline configuration.", m); discardedMessages ++; } BufUtil.release(m); } if (discardedMessages != 0) { logger.warn( "Discarded {} outbound message(s) that reached at the head of the pipeline. " + "Please check your pipeline configuration.", discardedMessages); } //其实最终还是调用usafe的flush来写数据 unsafe.flush(promise); } }嗯,这个还是比较简单的吧,说白了还是调用的channel的unsafe对象的flush方法来发送数据的,好了感觉这篇文章是给自己挖了一个坑啊。。我擦,还要去看unsafe对象的flush方法,好吧,那接下来来看NioSocketChannel的unsafe对象的flush方法吧,其定义在AbstractChannel里的AbstractUnsafe中
@Override //具体的将缓冲区的数据flush public void flush(final ChannelPromise promise) { if (eventLoop().inEventLoop()) { //如果已经有需要flush的任务了,那么将flush的任务挂起就好了 FlushTask task = flushTaskInProgress; if (task != null) { // loop over the tasks to find the last one for (;;) { FlushTask t = task.next; if (t == null) { break; } task = t.next; } task.next = new FlushTask(null, promise); return; } //here //调用这个方法 flushNotifierAndFlush(promise); } else { eventLoop().execute(new Runnable() { @Override public void run() { flush(promise); } }); } }代码还是很简单,好了接下来继续来看flushNotifierAndFlush方法的定义吧:
private void flushNotifierAndFlush(ChannelPromise promise) { flushNotifier(promise); //调用flush0方法来将数据发送数据 flush0(); }好吧,直接来看flush0方法吧:
private void flush0() { if (!inFlushNow) { // Avoid re-entrance try { // Flush immediately only when there's no pending flush. // If there's a pending flush operation, event loop will call flushNow() later, // and thus there's no need to call it now. if (!isFlushPending()) { //调用flushnow方法将数据flush出去 flushNow(); } } catch (Throwable t) { flushFutureNotifier.notifyFlushFutures(t); if (t instanceof IOException) { close(voidFuture()); } } } else { //执行flush的任务队列 if (!flushNowPending) { flushNowPending = true; eventLoop().execute(flushLaterTask); } } }这里分为两种情况吧,不过一般都是调用flushNowf方法将数据发送出去,不过当数据发送很慢的时候,还是可能将刮起的flush任务执行的,好了接下来来看flushNow方法吧:
@Override public final void flushNow() { if (inFlushNow || flushTaskInProgress != null) { return; } inFlushNow = true; //here //获取headhandler的context,也就是pipeline里面自定义的那个,是outboundhandler,从他那里来获取outbuf,也就是要发送的数据 ChannelHandlerContext ctx = headContext(); Throwable cause = null; try { if (metadata().bufferType() == BufType.BYTE) { ByteBuf out = ctx.outboundByteBuffer(); int oldSize = out.readableBytes(); //本身需要发送的数据量 try { //这个方法延后到了子类中显示 doFlushByteBuffer(out); } catch (Throwable t) { cause = t; } finally { int delta = oldSize - out.readableBytes(); //这里就相当于是已经发送了的数据量 out.discardSomeReadBytes(); flushFutureNotifier.increaseWriteCounter(delta); } } else { MessageBuf<Object> out = ctx.outboundMessageBuffer(); int oldSize = out.size(); try { doFlushMessageBuffer(out); } catch (Throwable t) { cause = t; } finally { flushFutureNotifier.increaseWriteCounter(oldSize - out.size()); } } if (cause == null) { flushFutureNotifier.notifyFlushFutures(); } else { flushFutureNotifier.notifyFlushFutures(cause); if (cause instanceof IOException) { close(voidFuture()); } } } finally { inFlushNow = false; } }这里比较需要注意的是,是获取整个pipeline的headhandler的context,将它的buf里面的数据发送出去,其实最终调用的还是doFlushByteBuffer方法来发送数据:AbstractNioByteChannel
//用于向channel中写数据 protected void doFlushByteBuffer(ByteBuf buf) throws Exception { for (int i = config().getWriteSpinCount() - 1; i >= 0; i --) { //其实是调用doWriteBytes来写数据,这个方法延后到了后来的类中 int localFlushedAmount = doWriteBytes(buf, i == 0); if (localFlushedAmount > 0) { break; } if (!buf.isReadable()) { // Reset reader/writerIndex to 0 if the buffer is empty. buf.clear(); break; } } }好了,还是来看doWriteBytes方法吧,它定义在NioSocketChannel中:
//写数据到channel里面,将buf里面的数据发送出去 protected int doWriteBytes(ByteBuf buf, boolean lastSpin) throws Exception { final int expectedWrittenBytes = buf.readableBytes(); //相当于是需要发送的数据 final int writtenBytes = buf.readBytes(javaChannel(), expectedWrittenBytes); //这里就是真正的发送数据 final SelectionKey key = selectionKey(); //获取selectionkey final int interestOps = key.interestOps(); //获取当前channel挂起的事件 if (writtenBytes >= expectedWrittenBytes) { //如果想要发送的数据都已经发送完了,那么可以更新感兴趣的事件了,将write事件去除 // Wrote the outbound buffer completely - clear OP_WRITE. if ((interestOps & SelectionKey.OP_WRITE) != 0) { key.interestOps(interestOps & ~SelectionKey.OP_WRITE); } } else { // Wrote something or nothing. // a) If wrote something, the caller will not retry. // - Set OP_WRITE so that the event loop calls flushForcibly() later. // b) If wrote nothing: // 1) If 'lastSpin' is false, the caller will call this method again real soon. // - Do not update OP_WRITE. // 2) If 'lastSpin' is true, the caller will not retry. // - Set OP_WRITE so that the event loop calls flushForcibly() later. //如果没有发送完数据,那么需要挂起写事件 if (writtenBytes > 0 || lastSpin) { if ((interestOps & SelectionKey.OP_WRITE) == 0) { key.interestOps(interestOps | SelectionKey.OP_WRITE); } } } return writtenBytes; }
好了,上面的代码还是很简单额,基本一看就能看明白,那么这个发送的流程也就走完了。。。
够长的。。感觉写的也不够仔细。。就这样吧。。
接下来可以看serverbootstrap的流程了。。。。
- java netty之一个write跟踪(数据发送)
- netty 发送 超过 1024 数据
- Netty学习之一--Java socket编程(单线程+多线程)
- 【Netty源码分析】发送数据过程
- java netty 服务端向客户端发送16进制数据,客户端无法接收-已解决
- netty学习之一:java.nio.ByteBuffer
- NB-IOT之一个完整的BC95 UDP从开机到数据发送接收过程
- java网络编程之Netty第一个程序(四)
- netty 学习 (3)发送对象
- netty 学习 (3)发送对象
- LAN9221网卡驱动分析之一 发送数据
- Netty学习之一
- Netty系列之一介绍
- Netty入门之一:用户指南
- 从c++到Java 之一(47个差异)
- java-Netty学习(2)
- netty学习之路(一) netty+protocolbuffer 实现简单的消息发送
- nio学习之netty入门(1)---发送字符串
- chrome浏览器扩展
- UIMenuController的使用,对UILabel拷贝以及定制菜单
- Linux下使用inotify监控文件动作
- STL---函数对象(仿函数)概述
- 关于 RMAN 备份 数据块 一致性的讨论
- java netty之一个write跟踪(数据发送)
- hadoop 命令
- Qt5下OpenGL程序的新写法
- UVaOJ10006 - Carmichael Numbers
- Android中设定EditText的输入长度
- 古堡算式
- jQuery源码
- 简单建一个android ndk工程(android环境+android-ndk-r8)
- 从温JavaSE基础知识的笔记