这是我参与11月更文挑战的第22天,活动详情查看:2021最后一次更文挑战
Channel常用方法
- close() 可以用来关闭Channel
- closeFuture() 用来处理 Channel 的关闭事件
- sync 方法作用是同步等待 Channel 关闭
- 而 addListener 方法是异步等待 Channel 关闭
- pipeline() 方法用于添加处理器
- write() 方法将数据写入
- 因为缓冲机制,数据被写入到 Channel 中的缓冲区以后,不会立即被发送
- 只有当缓冲满了或者调用了flush()方法后,才会将数据通过 Channel 发送出去
- writeAndFlush() 方法将数据写入并立即发送(刷出)
为什么需要sync()
在前两篇博客中Netty编程(一)—— 初识Netty+超全注释 - 掘金 (juejin.cn) 以及 Netty编程(二)—— EventLoop - 掘金 (juejin.cn)多次出现客户端部分的代码,而在其中有一句sync()
方法当时说是用来阻塞,在这一篇博客中首先解释一下这一句的必要性。
首先我们先给出来客户端的代码,和之前不用的是,这次不完全使用链式编码方式:
分析原因
如果我们将第31行的channelFuture.sync()
注释掉,服务端是接收不到发送的”hello world”的,下面来分析一下原因:
这是因为建立连接(connect)的过程是异步非阻塞的,
- 异步:调用connect的线程不关心结果
- 非阻塞:调用完connect就可以继续向下执行,不同等结果)
而connect
方法由main线程发起,但真正执行的是NioEventLoopGroup其中的某一个nio线程,主线程会继续向下执行。如果不通过sync()
方法阻塞主线程去等待连接真正建立,那么通过channelFuture.channel()
拿到的 Channel 对象,并不是真正与服务器建立好连接的 Channel,因为此时还没有成功建立连接,也就没法将信息正确的传输给服务器端。
解决方法
解决上面的方法有两种:
- 第一种方法就是一直使用
channelFuture.sync()
方法,阻塞住主线程,同步处理结果,等待连接真正建立好以后,再去获得 Channel 传递数据。使用该方法,获取 Channel 和发送数据的线程都是主线程 - 第二种方法可以使用addListener(回调对象)方法,它用于异步获取建立连接后的 Channel 和发送数据,使得执行这些操作的线程是 NIO 线程(去执行connect操作的线程),nio线程连接建立好了以后就会调用回调对象的operationComplete方法,下面以一个例子来解释这个方法:
1 | java复制代码public class MyClient { |
当客户端与服务端成功建立连接后,会执行addListener
方法中的operationComplete
方法,其中他传入的参数channelFuture是调用addListener
方法的channelFuture,之后执行其中的方法。值得注意的是,operationComplete
方法是在NIO线程中执行的,即由事件循环组负责。
处理关闭
当我们要关闭channel时,可以调用channel.close()方法进行关闭。但是该方法是一个异步方法。真正的关闭操作并不是在调用该方法的线程中执行的,而是在NIO线程中执行真正的关闭操作
如果我们想在channel真正关闭以后,执行一些额外的操作,可以选择以下两种方法来实现:
- 通过channel.closeFuture()方法获得对应的ChannelFuture对象,然后调用sync()方法阻塞执行操作的线程,等待channel真正关闭后,再执行其他操作
1 | ini复制代码// 获得closeFuture对象 |
- 调用closeFuture.addListener方法,添加close的后续操作
1 | csharp复制代码closeFuture.addListener(new ChannelFutureListener() { |
本文转载自: 掘金