在客户端代码中:
ChannelFuture channelFuture = new Bootstrap() .group(new NioEventLoopGroup()) .channel(NioSocketChannel.class) // 添加处理器: .handler(new ChannelInitializer<NioSocketChannel>() { // 连接建立完成之后被调用: @Override protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception { nioSocketChannel.pipeline().addLast(new StringEncoder()); } }) // 连接到服务器: .connect("localhost", 8080);
调用了connect()
方法,绑定了服务端的IP
地址和端口号之后,返回的是一个ChannelFuture
对象。同时connect()
是一个异步非阻塞方法,是由main
主线程进行调用执行,而真正执行的是由其他NIO
线程执行创建这个ChannelFuture
对象。所以,如果不调用channelFuture.sync();
阻塞等待,直接通过channel()
方法获取Channel
对象发送数据的话,服务端无法正常接收数据。
无法接受数据的原因是因为,创建线程的动作真正执行的不是main
主线程,所以当进行连接连接后,main
主线程无法得知客户端与服务端之间的连接是否已经建立完成,而是无阻塞的向下继续运行获取Channel
对象。
注意:如果类名带有Future
或者Promise
一般都需要配合异步方法进行执行,用于正确处理结果。
2.2.1 Sync 同步处理:
channelFuture.sync();
使用sync()
方法同步处理结果,也就是当主线程执行当前方法后,会阻塞当前线程,等待其他的NIO
线程完成客户端和服务端之间的连接建立完毕。
2.2.2 addLinstener 异步处理:
channelFuture.addListener(回调对象);
调用ChannelFuture
的addLinstener()
方法异步执行处理结果。
// 异步处理结果: channelFuture.addListener(new ChannelFutureListener() { // 在NIO线程把客户端与服务端之间的连接建立完成之后,就会调用operationComplete方法: @Override public void operationComplete(ChannelFuture channelFuture) throws Exception { // 在这个方法体中完成回调逻辑的处理: Channel channel = channelFuture.channel(); channel.writeAndFlush("hello !"); } });
2.2.3 Channel 关闭:
// 获取到关闭ChannelFuture对象: ChannelFuture closeFuture = channel.close();
有两种方式可以实现对Channel
的关闭操作:使用sync()
方法同步处理关闭;使用closeFuture
也就是ChannelFuture
对象实现异步处理关闭:
closeFuture.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture channelFuture) throws Exception { // 处理关闭后操作 } });
此时,在NioEventLoopGroup
中还有线程其实是未被关闭释放的,此时需要进行优雅关闭:
// 创建NioEventLoopGroup: NioEventLoopGroup group = new NioEventLoopGroup();
closeFuture.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture channelFuture) throws Exception { // 在这个回调方法中执行优雅关闭: group.shutdownGracefully(); } });