• Netty源码分析第3章(客户端接入流程)---->第3节: NioSocketChannel的创建


     

    Netty源码分析第三章: 客户端接入流程

     

    第三节: NioSocketChannel的创建

    回到上一小节的read()方法:

    public void read() {
        //必须是NioEventLoop方法调用的, 不能通过外部线程调用
        assert eventLoop().inEventLoop();
        //服务端channel的config
        final ChannelConfig config = config();
        //服务端channel的pipeline
        final ChannelPipeline pipeline = pipeline();
        //处理服务端接入的速率
        final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
        //设置配置
        allocHandle.reset(config);
        boolean closed = false;
        Throwable exception = null;
        try {
            try {
                do {
                    //创建jdk底层的channel
                    //readBuf用于临时承载读到链接
                    int localRead = doReadMessages(readBuf);
                    if (localRead == 0) {
                        break;
                    }
                    if (localRead < 0) {
                        closed = true;
                        break;
                    }
                    //分配器将读到的链接进行计数
                    allocHandle.incMessagesRead(localRead);
                    //连接数是否超过最大值
                } while (allocHandle.continueReading());
            } catch (Throwable t) {
                exception = t;
            }
            int size = readBuf.size();
            //遍历每一条客户端连接
            for (int i = 0; i < size; i ++) {
                readPending = false;
                //传递事件, 将创建NioSokectChannel进行传递
                //最终会调用ServerBootstrap的内部类ServerBootstrapAcceptor的channelRead()方法
                pipeline.fireChannelRead(readBuf.get(i));
            }
            readBuf.clear();
            allocHandle.readComplete();
            pipeline.fireChannelReadComplete();
            //代码省略
        } finally {
            //代码省略
        }
    }

    我们继续剖析int localRead = doReadMessages(readBuf)这一部分逻辑

    我们首先看readBuf:

    private final List<Object> readBuf = new ArrayList<Object>();

    这里只是简单的定义了一个ArrayList, doReadMessages(readBuf)方法就是将读到的链接放在这个list中, 因为这里是NioServerSocketChannel所以这走到了NioServerSocketChannel的doReadMessage()方法

    跟到doReadMessage()方法中:

    protected int doReadMessages(List<Object> buf) throws Exception {
        //根据当前jdk底层的serverSocketChannel拿到jdk底层channel
        SocketChannel ch = javaChannel().accept();
        try {
            if (ch != null) {
                //封装成一个NioSokectChannel扔到buf中
                buf.add(new NioSocketChannel(this, ch));
                return 1;
            }
        } catch (Throwable t) {
            //代码省略
        }
        return 0;
    }

    这里终于走到到了jdk底层相关的内容了

    首先根据jdk的ServerSocketChannel拿到jdk的Channel, 熟悉Nio的小伙伴应该不会陌生

    封装成一个NioSokectChannel扔到Readbuf中

    这里的NioSocketChannel是对jdk底层的SocketChannel的包装, 我们看到其构造方法传入两个参数, this代表当前NioServerSocketChannel, ch代表jdk的SocketChannel

    我们跟到NioSocketChannel的其造方法中:

    public NioSocketChannel(Channel parent, SocketChannel socket) {
        super(parent, socket);
        config = new NioSocketChannelConfig(this, socket.socket());
    }

    这里看到调用了父类构造方法, 传入两个参数, parent代表创建自身channel的, NioServerSocketChannel, socket代表jdk底层的socketChannel

    跟到父类构造方法中:

    protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) { 
        super(parent, ch, SelectionKey.OP_READ);
    }

    其中SelectionKey.OP_READ代表其监听事件是读事件

    继续跟父类的构造方法:

    protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
        super(parent);
        this.ch = ch;
        this.readInterestOp = readInterestOp;
        try {
            //设置为非阻塞
            ch.configureBlocking(false);
        } catch (IOException e) {
            //代码省略
        }
    }

    这里初始化了自身成员变量ch, 就是jdk底层的SocketChannel, 并初始化了自身的监听事件readInterestOp, 也就是读事件

    ch.configureBlocking(false)这一步熟悉nio的小伙伴也不陌生, 就是将jdk的SocketChannel设置为非阻塞

    我们继续跟到父类构造方法中:

    protected AbstractChannel(Channel parent) {
        this.parent = parent;
        id = newId();
        unsafe = newUnsafe();
        pipeline = newChannelPipeline();
    }

    这里初始化parent, 也就是创建自身的NioServerSocketChannel, 并为自身创建了唯一id

    初始化unsafe, 我们跟到newUnsafe()方法中

    由于此方法是NioEventLoop调用的, 所以会走到其父类AbstractNioByteChannel的newUnsafe()

    跟到newUnsafe()中:

    protected AbstractNioUnsafe newUnsafe() {
        return new NioByteUnsafe();
    }

    这里创建了NioByteUnsafe对象, 所以NioSocketChannel对应的unsafe是NioByteUnsafe

    继续往下跟, 我们看到其初始化了pipeline, 有关pipline的知识, 我们会在下一章节中讲到

    回到NioSocketChannel中的构造方法:

    public NioSocketChannel(Channel parent, SocketChannel socket) {
        super(parent, socket);
        config = new NioSocketChannelConfig(this, socket.socket());
    }

    同NioServerSocketChannel一样, 这里也初始化了一个Config属性, 传入两个参数, 当前NioSocketChannel自身和jdk的底层SocketChannel的socket对象

    我们跟进其构造方法:

    private NioSocketChannelConfig(NioSocketChannel channel, Socket javaSocket) {
        super(channel, javaSocket);
    }

    同样, 这个类是NioSocketChannel的内部类

    继续跟父类构造方法:

    public DefaultSocketChannelConfig(SocketChannel channel, Socket javaSocket) {
        super(channel);
        if (javaSocket == null) {
            throw new NullPointerException("javaSocket");
        }
        //保存当前javaSocket
        this.javaSocket = javaSocket;
        //是否禁止Nagle算法
        if (PlatformDependent.canEnableTcpNoDelayByDefault()) {
            try {
                setTcpNoDelay(true);
            } catch (Exception e) {
    
            }
        }
    }

    这里保存了SocketChannel的socket对象, 并且默认的情况禁止了Nagle算法, 有关Nagle, 感兴趣的同学可以学习下相关知识

    继续跟到父类构造方法中:

    public DefaultChannelConfig(Channel channel) {
        this(channel, new AdaptiveRecvByteBufAllocator());
    }

    又跟到到了我们熟悉的部分了, 也就是说, 无论NioServerSocketChannel和NioSocketChannel, 最后都会初始化DefaultChannelConfig, 并创建可变ByteBuf分配器, 我们之前小节对此做过详细剖析这里不再赘述, 这部分忘记的内容可以阅读之前小节内容进行回顾

    这个分配器什么时候真正分配字节缓冲的呢?我们会在之后的章节进行详细剖析

    至此我们剖析完成了NioSocketChannel的初始化过程

     

    上一节: 处理接入事件之handle的创建

    下一节: NioSocketChannel注册到selector

     

  • 相关阅读:
    找最后的字符
    统计指定数字和
    班级排队
    python编程题
    python函数题
    2019.6.24-2019.6.28(实训数据结构)3.数组编码求解问题
    2019.6.24-2019.6.28(实训数据结构)1.商品管理系统实训c++(实现的基本功能:初始化,创建表,插入,删除,更新,查询,链表数据与文件之间的转换)
    2019.6.24-2019.6.28(实训数据结构) 2.背包问题
    2019.6.24-2019.6.28(实训数据结构)4.树和二叉树应用项目
    2019.6.24-2019.6.28(实训数据结构)5.图的邻接矩阵表示
  • 原文地址:https://www.cnblogs.com/xiangnan6122/p/10204015.html
Copyright © 2020-2023  润新知