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();
}
}
}
以上代码主要做了以下几个事情:
- 声明了一个 EventLoopGroup 实例.
- 声明一个 Bootstrap 实例.
- 给定了一个 Channel 的类型.
- 设置数据的处理 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);
// 省略不必要的代码只说明初始化操作
}
看了上面的代码主要做了一下几件事情.
- 校验 nThreads 不指定的话默认值为 CPU 核心数 * 2
- 为成员属性 children 实例化指定一个 nThreads 长度的 EventExecutor 类型数组
- 初始化 children 数组中每个元素
- 为成员属性 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;
}
- 引入眼帘的首先是调用父类的构造器
- provider 赋值顾名思义这个属性应该就是创建 Selector 的功能。
- 下面就是声明一个 SelectorTuple 的局部变量从 openSelector(); 方法中返回,为 selector 赋值到这一步 Selector 已经是获取到了但是被 Netty 进行了封装成了 WindowsSelectorImpl 类型
- 为 unwrappedSelector 属性赋值
- 为 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 接口
- addTaskWakesUp: 为 true 时
- maxPendingTasks: 新任务被拒绝之前的最大待处理任务数
- executor: 执行线程的实例有一个 execute 方法
- taskQueue: 任务队列
- 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)(:storage54484bdb-a67b-434e-b759-cf0a9848924568bbeb1.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 完成了连接.
如果写的不足之处望指出谢谢各位观看!