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 的实例,我们继续看看这个构造器里做了些什么?

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.

[外链图片转存失败(img-rEnlazj3-1565062020632)(:storage\54484bdb-a67b-434e-b759-cf0a98489245\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 完成了连接.

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

Netty 源码学习——客户端流程分析的更多相关文章

  1. Netty 源码学习——EventLoop

    Netty 源码学习--EventLoop 在前面 Netty 源码学习--客户端流程分析中我们已经知道了一个 EventLoop 大概的流程,这一章我们来详细的看一看. NioEventLoopGr ...

  2. 【Netty源码学习】DefaultChannelPipeline(三)

    上一篇博客中[Netty源码学习]ChannelPipeline(二)我们介绍了接口ChannelPipeline的提供的方法,接下来我们分析一下其实现类DefaultChannelPipeline具 ...

  3. Netty源码学习系列之4-ServerBootstrap的bind方法

    前言 今天研究ServerBootstrap的bind方法,该方法可以说是netty的重中之重.核心中的核心.前两节的NioEventLoopGroup和ServerBootstrap的初始化就是为b ...

  4. 【Netty源码学习】ChannelPipeline(一)

    ChannelPipeline类似于一个管道,管道中存放的是一系列对读取数据进行业务操作的ChannelHandler. 1.ChannelPipeline的结构图: 在之前的博客[Netty源码学习 ...

  5. 【Netty源码学习】ServerBootStrap

    上一篇博客[Netty源码学习]BootStrap中我们介绍了客户端使用的启动服务,接下来我们介绍一下服务端使用的启动服务. 总体来说ServerBootStrap有两个主要功能: (1)调用父类Ab ...

  6. Netty源码解析—客户端启动

    Netty源码解析-客户端启动 Bootstrap示例 public final class EchoClient { static final boolean SSL = System.getPro ...

  7. HashMap的源码学习以及性能分析

    HashMap的源码学习以及性能分析 一).Map接口的实现类 HashTable.HashMap.LinkedHashMap.TreeMap 二).HashMap和HashTable的区别 1).H ...

  8. Dubbo源码学习--注册中心分析

    相关文章: Dubbo源码学习--服务是如何发布的 Dubbo源码学习--服务是如何引用的 注册中心 关于注册中心,Dubbo提供了多个实现方式,有比较成熟的使用zookeeper 和 redis 的 ...

  9. 【Netty源码学习】EventLoopGroup

    在上一篇博客[Netty源码解析]入门示例中我们介绍了一个Netty入门的示例代码,接下来的博客我们会分析一下整个demo工程运行过程的运行机制. 无论在Netty应用的客户端还是服务端都首先会初始化 ...

随机推荐

  1. The document cannot be opened. It has been renamed, deleted or moved.

    In the Individual components section of the Visual Studio Installer, make sure that Razor Language S ...

  2. .NET简介

    |   版权声明:本文为博主原创文章,未经博主允许不得转载. 微软跨语言运行的主要机制就如上图类似,其最重要的部分是CLR和MSIL:其中MSIL是微软中间语言,它的主要的作用是将不同 的语言,如:C ...

  3. shell编程:函数的返回值

    函数的返回值两种形式 return 0-255 0表示成功,1-255表示失败-------通常用于判断 echo 返回一个字符串------------通常用于返回一个执行的结果 return.sh ...

  4. 【外】001-python3之zip函数

    zip函数语法格式zip(可迭代对象1, 可迭代对象2, ... ,可迭代对象n) 函数接收任意个可迭代对象作为参数, 将所有对象中对应位置上的元素分别打包在一起组成一个tuple, 并将所有的tup ...

  5. windows xp .net framework 4.0 HttpWebRequest 报The underlying connection was closed,基础连接已关闭

    windows xp .net framework 4.0 HttpWebRequest 报The underlying connection was closed,基础连接已关闭,错误的解决方法 在 ...

  6. Spring mvc的执行流程

    一个请求匹配前端控制器 DispatcherServlet 的请求映射路径(在 web.xml中指定), WEB 容器将该请求转交给 DispatcherServlet 处理 DispatcherSe ...

  7. 忘记mysql超户密码的解决方法

    本文章针对用yum安装的mariadb数据库,如果是tar包安装的mysql数据库,只是数据库命令的关闭启动方式不同而已. 方法一:[root@localhost ~]# killall -u mys ...

  8. mongoose 常用数据库操作 删除

    删除 Model.remove(conditions, [callback]) try.js var User = require("./user.js"); function d ...

  9. Manacher模板(O(n)内求最长回文串长度)

    转自:https://segmentfault.com/a/1190000008484167 /* 由于回文分为偶回文(比如 bccb)和奇回文(比如 bcacb),而在处理奇偶问题上会比较繁琐,所以 ...

  10. Ecshop商品浏览历史样式修改方法

    想要修改ECSHOP的浏览历史样式,发现 history.lbi 中不能修改. 需要修改的文件:includes\lib_insert.php,找到函数:function insert_history ...