Bootstrap#connect
Bootstrap#connect
Bootstrap#doResolveAndConnect
AbstractBootstrap#initAndRegister
和ServerBootStrap一样,都调用的是父类AbstractBootstrap的final方法initAndRegister
BootStrap#doResolveAndConnect0
private ChannelFuture doResolveAndConnect0(final Channel channel, SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
try {
EventLoop eventLoop = channel.eventLoop();
AddressResolver<SocketAddress> resolver = this.resolver.getResolver(eventLoop);
if (!resolver.isSupported(remoteAddress) || resolver.isResolved(remoteAddress)) {
doConnect(remoteAddress, localAddress, promise);
return promise;
}
Future<SocketAddress> resolveFuture = resolver.resolve(remoteAddress);
if (resolveFuture.isDone()) {
Throwable resolveFailureCause = resolveFuture.cause();
if (resolveFailureCause != null) {
channel.close();
promise.setFailure(resolveFailureCause);
} else {
doConnect((SocketAddress)resolveFuture.getNow(), localAddress, promise);
}
return promise;
}
resolveFuture.addListener(new FutureListener<SocketAddress>() {
public void operationComplete(Future<SocketAddress> future) throws Exception {
if (future.cause() != null) {
channel.close();
promise.setFailure(future.cause());
} else {
Bootstrap.doConnect((SocketAddress)future.getNow(), localAddress, promise);
}
}
});
} catch (Throwable var9) {
promise.tryFailure(var9);
}
return promise;
}
BootStrap#doConnect
AbstractChannel#connect
跳转到PipeLineImpl处理链
connect是一个outbound事件,需要主动触发,从tail到head
DefaultChannelPipeline
我们已经分析过,Tail是一个TailContext的实例,而TailContext又是AbstractChannelHandlerContext的子类,并且没有实现connect()方法,因此这里调用的其实是AbstractChannelHandlerContext的connect()方法,我们看一下这个方法的实现代码。
public ChannelFuture connect(final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
if (remoteAddress == null) {
throw new NullPointerException("remoteAddress");
} else if (!this.validatePromise(promise, false)) {
return promise;
} else {
final AbstractChannelHandlerContext next = this.findContextOutbound();
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeConnect(remoteAddress, localAddress, promise);
} else {
safeExecute(executor, new Runnable() {
public void run() {
next.invokeConnect(remoteAddress, localAddress, promise);
}
}, promise, (Object)null);
}
return promise;
}
}
上面代码片段中有一句非常关键的代码,即finalAbstractChannelHandlerContext next=findContextOutbound(),这里调用findContextOutbound()方法,从DefaultChannelPipeline内的双向链表的Tail开始,不断向前找到第一个Outbound为true的AbstractChannelHandlerContext,然后调用它的invokeConnect()方法,代码如下。
调用它的invokeConnect(), 最终会跳转到HeadContext,而HeadContext的invokeConnect方法还是AbstractChannelHandlerContext的InvokerConnect,
AbstractChannleHandlerContext#invokerConnect
跳转到HeadContext的connect
AbstractNIoChannel.AbstractNioUnsafe#connect()
public final void connect(final SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
if (promise.setUncancellable() && this.ensureOpen(promise)) {
try {
if (AbstractNioChannel.this.connectPromise != null) {
throw new ConnectionPendingException();
}
boolean wasActive = AbstractNioChannel.this.isActive();
//调用doConnect方法
if (AbstractNioChannel.this.doConnect(remoteAddress, localAddress)) {
this.fulfillConnectPromise(promise, wasActive);
} else {
AbstractNioChannel.this.connectPromise = promise;
AbstractNioChannel.this.requestedRemoteAddress = remoteAddress;
int connectTimeoutMillis = AbstractNioChannel.this.config().getConnectTimeoutMillis();
if (connectTimeoutMillis > 0) {
AbstractNioChannel.this.connectTimeoutFuture = AbstractNioChannel.this.eventLoop().schedule(new Runnable() {
public void run() {
ChannelPromise connectPromise = AbstractNioChannel.this.connectPromise;
ConnectTimeoutException cause = new ConnectTimeoutException("connection timed out: " + remoteAddress);
if (connectPromise != null && connectPromise.tryFailure(cause)) {
AbstractNioUnsafe.this.close(AbstractNioUnsafe.this.voidPromise());
}
}
}, (long)connectTimeoutMillis, TimeUnit.MILLISECONDS);
}
promise.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isCancelled()) {
if (AbstractNioChannel.this.connectTimeoutFuture != null) {
AbstractNioChannel.this.connectTimeoutFuture.cancel(false);
}
AbstractNioChannel.this.connectPromise = null;
AbstractNioUnsafe.this.close(AbstractNioUnsafe.this.voidPromise());
}
}
});
}
} catch (Throwable var6) {
promise.tryFailure(this.annotateConnectException(var6, remoteAddress));
this.closeIfClosed();
}
}
}
调用Channel的doConnect
我们终于看到最关键的部分了,首先获取Java NIO的SocketChannel,然后获取NioSocketChannel的newSocket()方法返回的SocketChannel对象;再调用SocketChannel的connect()方法完成Java NIO底层的Socket连接。总结一下,客户端Bootstrap发起连接请求的流程可以用如下时序图直观地展示。