netty引导客户端启动类的代码如下
EventLoopGroup group = new NioEventLoopGroup();
try {
+ Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(
//new LoggingHandler(LogLevel.INFO),
new EchoClientHandler(firstMessageSize));
}
});
// Start the client.
ChannelFuture f = b.connect(host, port).sync();
// Wait until the connection is closed.
f.channel().closeFuture().sync();
} finally {
// Shut down the event loop to terminate all threads.
group.shutdownGracefully();
}
还是老样子,EventLoopGroup,handler这之类的在前几篇博文已经介绍过了,这次就着重跟踪ChannelFuture f = b.connect(host, port).sync(); 这行代码。下面是Boostrap类下的connect()方法:
public ChannelFuture connect() {
validate();
SocketAddress remoteAddress = this.remoteAddress;
if (remoteAddress == null) {
throw new IllegalStateException("remoteAddress not set");
}
return doConnect(remoteAddress, localAddress());
}
不得不说跟之前跟踪了的ServerBootstrap.bind()很像。validate()函数无非调用父类AbstractBootstrap的validate()方法,就是检查此Bootstrap对象是否设置了group、channelFactory这两个对象。
然后调用了doConnect创建连接
private ChannelFuture doConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
final ChannelPromise promise = channel.newPromise();
if (regFuture.isDone()) {
doConnect0(regFuture, channel, remoteAddress, localAddress, promise);
} else {
regFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
doConnect0(regFuture, channel, remoteAddress, localAddress, promise);
}
});
}
return promise;
}
若如果看过我之前服务端的连接,那么看这里肯定会非常亲切熟悉,首先通过initAndRegister()方法得到一个ChannelFuture的实例regFuture,通过regFuture可以看到channel注册过程的状态,通过regFuture.cause()方法判断是否在执行initAndRegister方法时产生来异常,若产生异常则return;最后通过regFuture等channel注册完成,再调用doConnect0();
我们再来具体分析下initAndRegister()这个方法。
final ChannelFuture initAndRegister() {
final Channel channel = channelFactory().newChannel();
try {
init(channel);
} catch (Throwable t) {
channel.unsafe().closeForcibly();
return channel.newFailedFuture(t);
}
ChannelFuture regFuture = group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}