IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Netty原理四:客户端Bootstrap启动连接时做了些什么? -> 正文阅读

[Java知识库]Netty原理四:客户端Bootstrap启动连接时做了些什么?

文章目录

前言

先来看一段很普通的客户端程序,定义 NioEventLoopGroup,然后通过 Bootstrap 去连接Server。

    public static void main(String[] args) throws Exception {
        //线程组
        NioEventLoopGroup group = new NioEventLoopGroup();
        //启动配置
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(group)   //设置线程组
                .channel(NioSocketChannel.class)   //设置客户通道实现类
                .handler(new ChannelInitializer<SocketChannel>() {      //创建一个通道初始化对象
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        System.out.println("在这里可以添加自定义的 Handler");
                    }
                });
        System.out.println("客户端已就绪,准备连接服务");

        //连接服务端,同步阻塞
        ChannelFuture future = bootstrap.connect("127.0.0.1", 7000).sync();

        //关闭连接
        future.channel().closeFuture().sync();
    }

这里跟Server程序有几个不同点:

  • 客户端只需要1个 NioEventLoopGroup,服务端需要2个 NioEventLoopGroup

  • 客户端使用 Bootstrap 启动连接,服务端使用 ServerBootstrap 启动监听,它们的继承关系如下图:
    在这里插入图片描述

  • 客户端调用 connect() 连接 Server,服务端调用 bind() 绑定监听IP+PORT

这边我们主要讲的就是 connect() 方法的内部实现。

PS.本篇文章Netty源码,只会贴出关键性的部分,方便理解。

原理解析

connect() 的核心就是 doResolveAndConnect():(不重要,pass)

    public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) {
        ObjectUtil.checkNotNull(remoteAddress, "remoteAddress");
        validate();
        //核心
        return doResolveAndConnect(remoteAddress, localAddress);
    }

doResolveAndConnect 关键的也就两个方法:initAndRegister、doResolveAndConnect0

    private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
    	//核心1
        final ChannelFuture regFuture = initAndRegister();
        final Channel channel = regFuture.channel();

        //核心2
        doResolveAndConnect0(channel, remoteAddress, localAddress, promise);
        return promise;
    }

1、initAndRegister: 初始化 NioSocketChannel,并注册到 NioEventLoopGroup 内部的 NioEventLoop

在这里插入图片描述

该方法的实现在基类 AbstractBootstrap.java,因此和服务端的启动基本一样,唯一不同的在于 init() 方法。用一张图来表示吧,比较清楚:

在这里插入图片描述

2、doResolveAndConnect0: 主要是调用 doConnect() 实现连接 Server

    private static void doConnect(
            final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise connectPromise) {

        final Channel channel = connectPromise.channel();
        channel.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                if (localAddress == null) {
                    channel.connect(remoteAddress, connectPromise);
                } else {
                    channel.connect(remoteAddress, localAddress, connectPromise);
                }
                connectPromise.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
            }
        });
    }

这里的连接需要调试跟踪,最终到达 AbstractNioChannel.java的内部类 AbstractNioUnsafe,执行此处的 connect 方法,下面我贴出几句关键代码:

       public final void connect(
                final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
           if (doConnect(remoteAddress, localAddress)) {
                fulfillConnectPromise(promise, wasActive);
            } else {
                //此处省略
            }
        }

继续跟踪 doConnect 方法,因为连接不是马上完成的,所以这边有个判断:如果连接尚未成功,则设置 interestOps == SelectionKey.OP_CONNECT

    protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
        boolean success = false;
        try {
            boolean connected = SocketUtils.connect(javaChannel(), remoteAddress);
            if (!connected) {
                selectionKey().interestOps(SelectionKey.OP_CONNECT);
            }
            success = true;
            return connected;
        } finally {
            if (!success) {
                doClose();
            }
        }
    }

既然设置了interestOps,那么 selector.select() 那边肯定会捕获到事件。
直接查看 NioEventLoop.java 的 processSelectedKey 方法,里面有个对 CONNECT 事件进行处理:

  • 1、将 SelectionKey.OP_CONNECT 移除掉,不需要再监听
  • 2、调用 unsafe.finishConnect()
            if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
                int ops = k.interestOps();
                ops &= ~SelectionKey.OP_CONNECT;
                k.interestOps(ops);

                unsafe.finishConnect();
            }

unsafe.finishConnect() 内部会调用 fulfillConnectPromise。 做了这么多,最最最关键的就是这里。此方法会触发 channelActive 事件,从而继续触发 AbstractNioChannel 的 doBeginRead 方法。看过 Netty原理一:ServerBootstrap启动过程全解析 这篇文章应该就记得,这里会重新设置 interestOps,设置的值就是 NioSocketChannel 构造函数设置的 SelectionKey.OP_READ

最后,连接成功之后,整个结构如下图所示

在这里插入图片描述

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-10-07 13:43:18  更:2021-10-07 13:43:45 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 19:16:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码