Netty客户端接入流程NioSocketChannel创建解析


Posted in Java/Android onMarch 25, 2022

前文传送门:Netty客户端处理接入事件handle创建

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的初始化过程

以上就是Netty客户端接入流程NioSocketChannel创建源码解析的详细内容,更多关于Netty客户端接入流程NioSocketChannel的资料请关注三水点靠木其它相关文章!

Java/Android 相关文章推荐
深入理解java.lang.String类的不可变性
Jun 27 Java/Android
Java中常用解析工具jackson及fastjson的使用
Jun 28 Java/Android
Java图书管理系统,课程设计必用(源码+文档)
Jun 30 Java/Android
Spring实现内置监听器
Jul 09 Java/Android
在Spring-Boot中如何使用@Value注解注入集合类
Aug 02 Java/Android
Java网络编程之UDP实现原理解析
Sep 04 Java/Android
java如何实现socket连接方法封装
Sep 25 Java/Android
Jpa Specification如何实现and和or同时使用查询
Nov 23 Java/Android
java代码实现空间切割
Jan 18 Java/Android
Java中的随机数Random
Mar 17 Java/Android
Java GUI编程菜单组件实例详解
Apr 07 Java/Android
解决Springboot PostMapping无法获取数据的问题
May 06 Java/Android
Java 超详细讲解设计模式之中的抽象工厂模式
Netty分布式客户端处理接入事件handle源码解析
Java 超详细讲解IO操作字节流与字符流
Netty分布式客户端接入流程初始化源码分析
Mar 25 #Java/Android
java后台调用接口及处理跨域问题的解决
Mar 24 #Java/Android
SpringBoot中使用Redis作为全局锁示例过程
Mar 24 #Java/Android
java项目构建Gradle的使用教程
Mar 24 #Java/Android
You might like
php5.4传引用时报错问题分析
2016/01/22 PHP
PHP编写登录验证码功能 附调用方法
2016/05/19 PHP
php字符串比较函数用法小结(strcmp,strcasecmp,strnatcmp及strnatcasecmp)
2016/07/18 PHP
PHP ajax+jQuery 实现批量删除功能实例代码小结
2018/12/06 PHP
extjs 列表框(multiselect)的动态添加列表项的方法
2009/07/31 Javascript
jquery+ajax每秒向后台发送请求数据然后返回页面的代码
2011/01/17 Javascript
jquery中push()的用法(数组添加元素)
2014/11/25 Javascript
浅谈jquery的map()和each()方法
2016/06/12 Javascript
基于BootStrap的Metronic框架实现页面链接收藏夹功能按钮移动收藏记录(使用Sortable进行拖动排序)
2016/08/29 Javascript
基于JS+Canves实现点击按钮水波纹效果
2016/09/15 Javascript
详解Node.js:events事件模块
2016/11/24 Javascript
自动适应iframe右边的高度
2016/12/22 Javascript
微信小程序(六):列表上拉加载下拉刷新示例
2017/01/13 Javascript
微信小程序page的生命周期和音频播放及监听实例详解
2017/04/07 Javascript
JavaScript基本语法_动力节点Java学院整理
2017/06/26 Javascript
js时间戳与日期格式之间转换详解
2017/12/11 Javascript
Angular2学习笔记之数据绑定的示例代码
2018/01/03 Javascript
nodejs基于express实现文件上传的方法
2018/03/19 NodeJs
详解在vue-cli项目下简单使用mockjs模拟数据
2018/10/19 Javascript
详解三种方式解决vue中v-html元素中标签样式
2018/11/22 Javascript
vue打包静态资源后显示空白及static文件路径报错的解决
2020/09/02 Javascript
[02:23]DOTA2英雄基础教程 幻影长矛手
2013/12/09 DOTA
[12:29]《一刀刀一天》之DOTA全时刻19:蝙蝠骑士田伯光再度不举
2014/06/10 DOTA
[58:59]完美世界DOTA2联赛PWL S3 access vs CPG 第一场 12.13
2020/12/16 DOTA
[47:03]完美世界DOTA2联赛PWL S3 access vs LBZS 第一场 12.20
2020/12/23 DOTA
Python实现感知器模型、两层神经网络
2017/12/19 Python
python一行sql太长折成多行并且有多个参数的方法
2018/07/19 Python
Python爬虫 scrapy框架爬取某招聘网存入mongodb解析
2019/07/31 Python
python数据库操作mysql:pymysql、sqlalchemy常见用法详解
2020/03/30 Python
推荐10个CSS3 制作的创意下拉菜单效果
2014/02/11 HTML / CSS
如何在网站上添加谷歌定位信息
2016/04/16 HTML / CSS
Casadei卡萨蒂官网:意大利奢侈鞋履品牌
2017/10/28 全球购物
具有防紫外线功能的高性能钓鱼服装:Hook&Tackle
2018/08/16 全球购物
党员教师个人对照检查材料范文
2014/09/25 职场文书
机关干部作风建设剖析材料
2014/10/23 职场文书
Debian11 Xfce终端光标的颜色怎么设置?
2022/08/14 数码科技