Netty 源码学习——客户端流程分析

Netty 源码学习——客户端流程分析


友情提醒: 需要观看者具备一些 NIO 的知识,否则看起来有的地方可能会不明白。

使用版本依赖

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.36.Final</version>
</dependency>

io.netty.bootstrap.Bootstrap

一个 Bootstrap 可以轻松来使用 Netty 来构建客户端代码。本文只是说明一下 Netty 客户端的运行流程,并不会对细节模块进行解析说明。

客户端部分

public class NioClient {
	public static void main(String[] args) {
		EventLoopGroup group = new NioEventLoopGroup();
		try {
			Bootstrap bootstrap = new Bootstrap();
			bootstrap.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {
				@Override protected void initChannel(SocketChannel ch) throws Exception {
					ChannelPipeline pipeline = ch.pipeline();
					pipeline.addLast("stringDecoder", new StringDecoder());
					pipeline.addLast("stringEncoder", new StringEncoder());
					pipeline.addLast("clientHandle", new SimpleChannelInboundHandler<String>() {
						@Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
						}
					});
				}
			});
			ChannelFuture future = bootstrap.connect("127.0.0.1", 9898).sync();
			future.channel().closeFuture().sync();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			group.shutdownGracefully();
		}
	}
}

以上代码主要做了以下几个事情:

  1. 声明了一个 EventLoopGroup 实例.
  2. 声明一个 Bootstrap 实例.
  3. 给定了一个 Channel 的类型.
  4. 设置数据的处理 Handle.

EventLoopGroup 的实例化过程

我们来进去看一看下面 new NioEventLoopGroup() 到底发生了什么。

EventLoopGroup group = new NioEventLoopGroup();

首先我们需要看下 NioEventLoopGroup 的继承结构。
在这里插入图片描述

进入 new NioEventLoopGroup(); 构造器后发现会有好几个重载的构造器的调用,但是我们一步步点进去发现最后我们跟到了 MultithreadEventExecutorGroup 中.

protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                        EventExecutorChooserFactory chooserFactory, Object... args) {
    if (nThreads <= 0) {
        throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
    }

    if (executor == null) {
        executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
    }

    children = new EventExecutor[nThreads];

    for (int i = 0; i < nThreads; i ++) {
        try {
            children[i] = newChild(executor, args);
        } catch (Exception e) {
            // 省略不必要的代码只说明初始化操作
        } finally {
            // 省略不必要的代码只说明初始化操作
        }
    }

    chooser = chooserFactory.newChooser(children);
    // 省略不必要的代码只说明初始化操作
}

看了上面的代码主要做了一下几件事情.

  1. 校验 nThreads 不指定的话默认值为 CPU 核心数 * 2
  2. 为成员属性 children 实例化指定一个 nThreads 长度的 EventExecutor 类型数组
  3. 初始化 children 数组中每个元素
  4. 为成员属性 chooser 初始化

初始化 children 数组中每个元素

@Override
    protected EventLoop newChild(Executor executor, Object... args) throws Exception {
        return new NioEventLoop(this, executor, (SelectorProvider) args[0],
            ((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
    }

我们发现返回的是一个 NioEventLoop 的实例,我们继续看看这个构造器里做了些什么?

扫描二维码关注公众号,回复: 6567813 查看本文章
NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
                 SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
        super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
        // 省略不必要的代码
        provider = selectorProvider;
        final SelectorTuple selectorTuple = openSelector();
        selector = selectorTuple.selector;
        unwrappedSelector = selectorTuple.unwrappedSelector;
        selectStrategy = strategy;
    }
  1. 引入眼帘的首先是调用父类的构造器
  2. provider 赋值顾名思义这个属性应该就是创建 Selector 的功能。
  3. 下面就是声明一个 SelectorTuple 的局部变量从 openSelector(); 方法中返回,为 selector 赋值到这一步 Selector 已经是获取到了但是被 Netty 进行了封装成了 WindowsSelectorImpl 类型
  4. 为 unwrappedSelector 属性赋值
  5. 为 selectStrategy 属性赋值。
    注意: 这个时候 NioEventLoop 中已经持有 selector 的实例了。

我们继续看 EventLoop 继承结构找到 SingleThreadEventExecutor 的类

protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
                                        boolean addTaskWakesUp, int maxPendingTasks,
                                        RejectedExecutionHandler rejectedHandler) {
        super(parent);
        this.addTaskWakesUp = addTaskWakesUp;
        this.maxPendingTasks = Math.max(16, maxPendingTasks);
        this.executor = ThreadExecutorMap.apply(executor, this);
        taskQueue = newTaskQueue(this.maxPendingTasks);
        rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
    }

其实看过线程池的会发现有些眼熟,因为最上层的父类实现了 ExecutorService 接口

  1. addTaskWakesUp: 为 true 时
  2. maxPendingTasks: 新任务被拒绝之前的最大待处理任务数
  3. executor: 执行线程的实例有一个 execute 方法
  4. taskQueue: 任务队列
  5. rejectedExecutionHandler: 拒绝策略

那么一个 NioEventLoop 实例就已经创建好了。那么 MultithreadEventExecutorGroup 类的 children 属性也就初始化好了。下面我们就要初始化 chooser 这个属性.其实很简单,如果线程池的数量是 2 幂则创建 PowerOfTwoEventExecutorChooser 否则创建 GenericEventExecutorChooser 他们的作用就是在 children 选择出一个来处理请求。

@Override
    public EventExecutorChooser newChooser(EventExecutor[] executors) {
        if (isPowerOfTwo(executors.length)) {
            return new PowerOfTwoEventExecutorChooser(executors);
        } else {
            return new GenericEventExecutorChooser(executors);
        }
    }

为什么如果是 2 的幂创建 PowerOfTwoEventExecutorChooser 呢,我们来看里面 next 方法,这样的话用 & 运算来代替 % 运算效率会高。真是不放过一丝一毫提升效率的机会啊。

private static final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser {
        private final AtomicInteger idx = new AtomicInteger();
        private final EventExecutor[] executors;

        PowerOfTwoEventExecutorChooser(EventExecutor[] executors) {
            this.executors = executors;
        }

        @Override
        public EventExecutor next() {
            return executors[idx.getAndIncrement() & executors.length - 1];
        }
    }

chooser 创建好后我们客户端的 EventLoopGroup 也就创建完毕了。

NioSocketChannel 的初始化过程

我们先来看一看 NioSocketChannel 的继承结构图
在这里插入图片描述

Bootstrap bootstrap = new Bootstrap();
			bootstrap.group(group).channel(NioSocketChannel.class);

我们发现后面就是调用 channel 方法来传入一个 NioSocketChannel.class 那一定必须要一探究竟了。

public B channel(Class<? extends C> channelClass) {
    if (channelClass == null) {
        throw new NullPointerException("channelClass");
    }
    return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}

首先实例化了 ReflectiveChannelFactory 对象出来。

public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {
    private final Constructor<? extends T> constructor;

    public ReflectiveChannelFactory(Class<? extends T> clazz) {
        ObjectUtil.checkNotNull(clazz, "clazz");
        try {
            this.constructor = clazz.getConstructor();
        } catch (NoSuchMethodException e) {
           
        }
    }

    @Override
    public T newChannel() {
        try {
            return constructor.newInstance();
        } catch (Throwable t) {
            throw new ChannelException("Unable to create Channel from class " + constructor.getDeclaringClass(), t);
        }
    }
}

我们通过代码发现通过 ReflectiveChannelFactory 构造器将 NioSocketChannel.class 的对应构造器对象赋值给了 ReflectiveChannelFactory 中的 constructor.再往下看 newChannel() 方法不就是用构造器反射的方式创建出 NioSocketChannel 实例嘛.

紧接着创建好 ReflectiveChannelFactory 实例后当作参数传入 channelFactory(ChannelFactory channelFactory); 方法中,其实就是将 ReflectiveChannelFactory 的实例赋值给 AbstractBootstrap 的 channelFactory 属性而已

@Deprecated
    public B channelFactory(ChannelFactory<? extends C> channelFactory) {
        this.channelFactory = channelFactory;
        return self();
    }

NioSocketChannel 实例化的时机
这个时候 bootstrap 差不多也设置完了,我们也知道了 channel 的类型是什么其实就是 NioSocketChannel.那也只是一个 ReflectiveChannelFactory 的实例而已,那么什么时候会利用这个 ReflectiveChannelFactory 来实例化 channel 呢,其实就是在 bootstrap.connect 中.

hannelFuture future = bootstrap.connect("127.0.0.1", 9898).sync();

我们来跟进去发现一个很关键的方法 initAndRegister();

private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
    final ChannelFuture regFuture = initAndRegister();
}

在 initAndRegister(); 中发现了实例化的方法

final ChannelFuture initAndRegister() {
    Channel channel = null;
    try {
        // 发现在这里执行了实例化的操作
        channel = channelFactory.newChannel();
        init(channel);
    } catch (Throwable t) {
    }
    return regFuture;
}

执行了 channelFactory.newChannel(); 后自然会调用 NioSocketChannel 的无参构造器。

private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();

public NioSocketChannel() {
    this(DEFAULT_SELECTOR_PROVIDER);
}

我们发现调用内部构造器的时候传入了 SelectorProvider 实例,我们继续看发现了一个 newSocket 的方法并且将 SelectorProvider 实例传入了进去。

public NioSocketChannel(SelectorProvider provider) {
    this(newSocket(provider));
}

private static SocketChannel newSocket(SelectorProvider provider) {
        try {
            // 终于发现了创建了一个 SocketChannel
            return provider.openSocketChannel();
        } catch (IOException e) {
            throw new ChannelException("Failed to open a socket.", e);
        }
    }

但是内部继续讲 SocketChannel 给传入了下一个构造器

public NioSocketChannel(SocketChannel socket) {
    this(null, socket);
}

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

好我们发现调用了 super 父类的构造器传入了 parent 是一个 null 和刚才创建的 SocketChannel.

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

发现它也调用了自己父类的构造方法

protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
    // 继续调用父类构造器
    super(parent);
    // 将 SocketChannel 赋值给 ch
    this.ch = ch;
    // SelectionKey.OP_READ 的值
    this.readInterestOp = readInterestOp;
    try {
        // 将 SocketChannel 设置为非阻塞的
        ch.configureBlocking(false);
    } catch (IOException e) {
    }
}
protected AbstractChannel(Channel parent) {
    this.parent = parent;
    // id 赋值
    id = newId();
    // Unsafe 赋值实例为 NioSocketChannelUnsafe
    unsafe = newUnsafe();
    // 设置一个管道实例为 DefaultChannelPipeline
    pipeline = newChannelPipeline();
}

Unsafe 是啥呢?我们接着来看从注释中可以大致看出,会对应到相关的 Java 底层的 Socket 的操作.

/**
 * <em>Unsafe</em> operations that should <em>never</em> be called from user-code. These methods
 * are only provided to implement the actual transport, and must be invoked from an I/O thread except for the
 * following methods:
 * <ul>
 *   <li>{@link #localAddress()}</li>
 *   <li>{@link #remoteAddress()}</li>
 *   <li>{@link #closeForcibly()}</li>
 *   <li>{@link #register(EventLoop, ChannelPromise)}</li>
 *   <li>{@link #deregister(ChannelPromise)}</li>
 *   <li>{@link #voidPromise()}</li>
 * </ul>
 */
interface Unsafe {
    RecvByteBufAllocator.Handle recvBufAllocHandle();
    SocketAddress localAddress();
    SocketAddress remoteAddress();
    void register(EventLoop eventLoop, ChannelPromise promise);
    void bind(SocketAddress localAddress, ChannelPromise promise);
    void connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise);
    void disconnect(ChannelPromise promise);
    void close(ChannelPromise promise);
    void closeForcibly();
    void deregister(ChannelPromise promise);
    void beginRead();
    void write(Object msg, ChannelPromise promise);
    void flush();
    ChannelPromise voidPromise();
    ChannelOutboundBuffer outboundBuffer();
}

至此一个完整的 NioSocketChannel 就初始化完成了.

Channel 的注册过程

上一步我们了解了 Channel 的初始化过程,下面这个 channel 什么时候注册到 Selector 中呢?
我们继续看跟进 bootstrap.connect() 代码。
发现最后跟进的到了 io.netty.bootstrap.Bootstrap#doResolveAndConnect() 方法中调用了内部的 initAndRegister(); 方法.

private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
    final ChannelFuture regFuture = initAndRegister();
        return promise;
    }
}

从这个方法名字就可以看出初始化并注册。我们接着往下看

final ChannelFuture initAndRegister() {
    Channel channel = null;
    try {
        channel = channelFactory.newChannel();
        init(channel);
    } catch (Throwable t) {
    }

    ChannelFuture regFuture = config().group().register(channel);
    if (regFuture.cause() != null) {
        if (channel.isRegistered()) {
            channel.close();
        } else {
            channel.unsafe().closeForcibly();
        }
    }
    return regFuture;
}

之前我们已经看完了 channelFactory.newChannel(); 中到底发生了什么事情,现在我们可以继续升级往下看 ChannelFuture regFuture = config().group().register(channel); 这一步到底发生了什么。

首先回去了对应的 group 这个 group 我们显然最开始已经初始化好了是一个 NioEventLoopGroup 并调用了 register() 方法.

@Override
public ChannelFuture register(Channel channel) {
    return next().register(channel);
}

先调用了 next() 函数,这个就是根据 PowerOfTwoEventExecutorChooser 还是 GenericEventExecutorChooser 来选择一个 group 执行事情。既然找到了合适的 group 那么接下来就该执行 register 了.

@Override
public ChannelFuture register(Channel channel) {
    return register(new DefaultChannelPromise(channel, this));
}

在执行之前先实例化了 DefaultChannelPromise 将 channel 和 this 传递进去保存在 DefaultChannelPromise 实例中,此时 this 就是 group 的实例。

public DefaultChannelPromise(Channel channel, EventExecutor executor) {
    super(executor);
    this.channel = checkNotNull(channel, "channel");
}
@Override
public ChannelFuture register(final ChannelPromise promise) {
    ObjectUtil.checkNotNull(promise, "promise");
    promise.channel().unsafe().register(this, promise);
    return promise;
}

在 register 中我们发现了,通过 promise.channel() 中的 unsafe 引用调用了 register 方法,此时的 unsafe 实例类型是 NioSocketChannelUnsafe.感觉已经快要看到我们想要看到的东西了,继续前进.

@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
    AbstractChannel.this.eventLoop = eventLoop;

    if (eventLoop.inEventLoop()) {
        register0(promise);
    } else {
        try {
            eventLoop.execute(new Runnable() {
                @Override
                public void run() {
                    register0(promise);
                }
            });
        } catch (Throwable t) {
        }
    }
}

结果在这个方法里我们发现了使用 eventLoop 开启了一个任务去执行并且将 ChannelPromise 传了进去。突然发现怎么还没到我们想看到的地方,看源码就是这样绕来绕去有时候自己都不知道绕到了哪里,但是也要硬着头皮去看,不看永远就不知道了,我们继续往下看.

private void register0(ChannelPromise promise) {
    try {
        if (!promise.setUncancellable() || !ensureOpen(promise)) {
            return;
        }
        boolean firstRegistration = neverRegistered;
        doRegister();
        neverRegistered = false;
        registered = true;
        pipeline.invokeHandlerAddedIfNeeded();

        safeSetSuccess(promise);
        pipeline.fireChannelRegistered();

        if (isActive()) {
            if (firstRegistration) {
                pipeline.fireChannelActive();
            } else if (config().isAutoRead()) {

                beginRead();
            }
        }
    } catch (Throwable t) {
    }
}

上面代码我们发现在里面竟然还调用了 doRegister(); 不过通过名称感觉 do 开头的函数往往就是真正干事情的,其他的函数名感觉就是烟雾弹一层套一层,点进去我们发现是一个空方法等待子类去重写,这个不就是模板方法模式吗,顺便还学习了一下设计模式,那我们去子类实现里看看.

@Override
protected void doRegister() throws Exception {
    boolean selected = false;
    for (;;) {
        try {
            selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
            return;
        } catch (CancelledKeyException e) {
            if (!selected) {
                eventLoop().selectNow();
                selected = true;
            } else {
                throw e;
            }
        }
    }
}

终于我们看到了想要看的东西 javaChannel().register(eventLoop().unwrappedSelector(), 0, this); 使用了之前我们初始化 NioSocketChannel 时创建的 java.nio.channels.SocketChannel 实例执行了 register 传入了之前获取到的 selector 实例注册了进去.

总的来说, Channel 注册过程所做的工作就是将 Channel 与对应的 EventLoop 关联, 因此这也体现了, 在 Netty 中, 每个 Channel 都会关联一个特定的 EventLoop, 并且这个 Channel 中的所有 IO 操作都是在这个 EventLoop 中执行的; 当关联好 Channel 和 EventLoop 后, 会继续调用底层的 Java NIO SocketChannel 的 register 方法, 将底层的 java.nio.channels.SocketChannel 注册到指定的 selector 中. 通过这两步, 就完成了 Netty Channel 的注册过程.

客户端连接

经过前面的该初始化的初始化,该赋值的也都赋值完毕了,终于到了我们的最后一步连接到对应的服务器了.
我们就直奔主题, 分析一下客户端是如何发起 TCP 连接的.首先还是继续回到我们的 bootstrap.connect() 方法中.继续跟进到 doResolveAndConnect(); 方法.

private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
    // 初始化并注册 channel
    final ChannelFuture regFuture = initAndRegister();
    // 获取到 channel
    final Channel channel = regFuture.channel();

    // 如果异步操作完成
    if (regFuture.isDone()) {
        if (!regFuture.isSuccess()) {
            return regFuture;
        }
        // 执行注册方法
        return doResolveAndConnect0(channel, remoteAddress, localAddress, channel.newPromise());
    } else {
        final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
        // 添加监听事件
        regFuture.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                Throwable cause = future.cause();
                if (cause != null) {

                    promise.setFailure(cause);
                } else {

                    promise.registered();
                    // 执行注册方法
                    doResolveAndConnect0(channel, remoteAddress, localAddress, promise);
                }
            }
        });
        return promise;
    }
}

上面代码发现最终都会调用 doResolveAndConnect0() 我们跟进去

private ChannelFuture doResolveAndConnect0(final Channel channel, SocketAddress remoteAddress,
                                               final SocketAddress localAddress, final ChannelPromise promise) {
        try {
            // 先获取到 eventLoop
            final EventLoop eventLoop = channel.eventLoop();
            // 封装一个 AddressResolver 对象
            final AddressResolver<SocketAddress> resolver = this.resolver.getResolver(eventLoop);

            if (!resolver.isSupported(remoteAddress) || resolver.isResolved(remoteAddress)) {
                doConnect(remoteAddress, localAddress, promise);
                return promise;
            }
            // 异步执行操作
            final Future<SocketAddress> resolveFuture = resolver.resolve(remoteAddress);

            if (resolveFuture.isDone()) {
                final Throwable resolveFailureCause = resolveFuture.cause();
                if (resolveFailureCause != null) {
                } else {
                    // 重点
                    doConnect(resolveFuture.getNow(), localAddress, promise);
                }
                return promise;
            }

            resolveFuture.addListener(new FutureListener<SocketAddress>() {
                @Override
                public void operationComplete(Future<SocketAddress> future) throws Exception {
                    if (future.cause() != null) {
                        channel.close();
                        promise.setFailure(future.cause());
                    } else {
                        // 重点
                        doConnect(future.getNow(), localAddress, promise);
                    }
                }
            });
        } catch (Throwable cause) {
            promise.tryFailure(cause);
        }
        return promise;
    }

我们发现调用到 doConnect() 方法

private static void doConnect(
            final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise connectPromise) {
    // 获取对应的 NioSocketChannel 实例
    final Channel channel = connectPromise.channel();
    // 使用对应的 Loop 开启一个线程任务在线程中执行连接
    channel.eventLoop().execute(new Runnable() {
        @Override
        public void run() {
            if (localAddress == null) {
                // 使用 NioSocketChannel.connect()
                channel.connect(remoteAddress, connectPromise);
            } else {
                channel.connect(remoteAddress, localAddress, connectPromise);
            }
            connectPromise.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
        }
    });
}

后面使用了 channel 中的 pipeline 属性的 connect(); 并且在 pipeline 中使用的 tail 属性的 connect();

 @Override
public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
    return pipeline.connect(remoteAddress, promise);
}

@Override
public final ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
    return tail.connect(remoteAddress, promise);
}

我们继续跟进

@Override
public ChannelFuture connect(
        final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
    // 查找到对应的 handler
    final AbstractChannelHandlerContext next = findContextOutbound(MASK_CONNECT);
    EventExecutor executor = next.executor();
    if (executor.inEventLoop()) {
        next.invokeConnect(remoteAddress, localAddress, promise);
    } else {
        safeExecute(executor, new Runnable() {
            @Override
            public void run() {
                next.invokeConnect(remoteAddress, localAddress, promise);
            }
        }, promise, null);
    }
    return promise;
}

上面的代码中有一个关键的地方, 即 final AbstractChannelHandlerContext next = findContextOutbound(), 这里调用 findContextOutbound 方法, 从 DefaultChannelPipeline 内的双向链表的 tail 开始, 不断向前寻找第一个 outbound 为 true 的 AbstractChannelHandlerContext, 然后调用它的 invokeConnect 方法.现在我们不要纠结于这里后面的篇章我们会详细解释 Netty 的 ChannelPipeline 的实现机制.现在只需要了解到这个方法是做什么的.

之后根据查找出的 AbstractChannelHandlerContext 实例执行对应的 invokeConnect(); 方法参数是需要连接到的 ip, null, promise 实例中包含了 channel.
b68bbeb1.png

private void invokeConnect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
    if (invokeHandler()) {
        try {
            ((ChannelOutboundHandler) handler()).connect(this, remoteAddress, localAddress, promise);
        } catch (Throwable t) {
            notifyOutboundHandlerException(t, promise);
        }
    } else {
        connect(remoteAddress, localAddress, promise);
    }
}

上面代码进行了一次强转之后调用了 connect(); 继续点进去看看

@Override
public void connect(
        ChannelHandlerContext ctx,
        SocketAddress remoteAddress, SocketAddress localAddress,
        ChannelPromise promise) {
    unsafe.connect(remoteAddress, localAddress, promise);
}

我们看到了 unsafe 对象仿佛胜利就在眼前了,接着加把劲看下去.

@Override
public final void connect(
        final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
    if (!promise.setUncancellable() || !ensureOpen(promise)) {
        return;
    }

    try {
        if (connectPromise != null) {
            // Already a connect in process.
            throw new ConnectionPendingException();
        }

        boolean wasActive = isActive();
        // 执行连接操作
        if (doConnect(remoteAddress, localAddress)) {
            fulfillConnectPromise(promise, wasActive);
        } else {
            // 省略一些代码
        }
    } catch (Throwable t) {
        promise.tryFailure(annotateConnectException(t, remoteAddress));
        closeIfClosed();
    }
}

在里面还调用了doConnect();

@Override
protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
    boolean success = false;
    try {
        // 使用工具类连接,传入 channel 和目标 ip
        boolean connected = SocketUtils.connect(javaChannel(), remoteAddress);
        if (!connected) {
            // 增加一个 CONNECT 事件
            selectionKey().interestOps(SelectionKey.OP_CONNECT);
        }
        success = true;
        return connected;
    } finally {
        if (!success) {
            doClose();
        }
    }
}

我们来跟进 SocketUtils.connect();

public static boolean connect(final SocketChannel socketChannel, final SocketAddress remoteAddress)
            throws IOException {
    try {
        // 不用做权限检查
        return AccessController.doPrivileged(new PrivilegedExceptionAction<Boolean>() {
            @Override
            public Boolean run() throws IOException {
                // 最终使用了 socketChannel.connect() 来连接到目标
                return socketChannel.connect(remoteAddress);
            }
        });
    } catch (PrivilegedActionException e) {
        throw (IOException) e.getCause();
    }
}

最后我们终于看到的最关键的部分了, 庆祝一下! 最后还是调用了 java.nio.channels.SocketChannel 完成了连接.

如果写的不足之处望指出谢谢各位观看!

猜你喜欢

转载自blog.csdn.net/qq_16830879/article/details/93308234