Channel
Channel 的常用方法
- close() 可以用来关闭Channel
- closeFuture() 用来处理 Channel 的关闭
- sync 方法作用是同步等待 Channel 关闭
- addListener 方法是异步等待 Channel 关闭
- pipeline() 方法用于添加处理器
- write() 方法将数据写入
- 因为缓冲机制,数据被写入到 Channel 中以后,不会立即被发送
- 只有当缓冲满了或者调用了flush()方法后,才会将数据通过 Channel 发送出去
- writeAndFlush() 方法将数据写入并立即发送(刷出)
public class MyClient { public static void main(String[] args) throws IOException, InterruptedException { ChannelFuture channelFuture = new Bootstrap() .group(new NioEventLoopGroup()) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast(new StringEncoder()); } }) // 该方法为异步非阻塞方法,主线程调用后不会被阻塞,真正去执行连接操作的是NIO线程 // NIO线程:NioEventLoop 中的线程 .connect(new InetSocketAddress("localhost", 8080)); // 该方法用于等待连接真正建立 channelFuture.sync(); // 获取客户端-服务器之间的Channel对象 Channel channel = channelFuture.channel(); channel.writeAndFlush("hello world"); System.in.read(); } }
因为建立连接(connect)的过程是异步非阻塞的,若不通过sync()方法阻塞主线程,等待连接真正建立,这时通过 channelFuture.channel() 拿到的 Channel 对象,并不是真正与服务器建立好连接的 Channel,也就没法将信息正确的传输给服务器端
所以需要通过channelFuture.sync()方法,阻塞主线程,同步处理结果,等待连接真正建立好以后,再去获得 Channel 传递数据。使用该方法,获取 Channel 和发送数据的线程都是主线程
addListener方法
通过这种方法可以在NIO线程中获取 Channel 并发送数据,而不是在主线程中执行这些操作
public class MyClient { public static void main(String[] args) throws IOException, InterruptedException { ChannelFuture channelFuture = new Bootstrap() .group(new NioEventLoopGroup()) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast(new StringEncoder()); } }) // 该方法为异步非阻塞方法,主线程调用后不会被阻塞,真正去执行连接操作的是NIO线程 // NIO线程:NioEventLoop 中的线程 .connect(new InetSocketAddress("localhost", 8080)); // 当connect方法执行完毕后,也就是连接真正建立后 // 会在NIO线程中调用operationComplete方法 channelFuture.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture channelFuture) throws Exception { Channel channel = channelFuture.channel(); channel.writeAndFlush("hello world"); } }); System.in.read(); } }
关闭Channel
public class ReadClient { public static void main(String[] args) throws InterruptedException { // 创建EventLoopGroup,使用完毕后关闭 NioEventLoopGroup group = new NioEventLoopGroup(); ChannelFuture channelFuture = new Bootstrap() .group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast(new StringEncoder()); } }) .connect(new InetSocketAddress("localhost", 8080)); channelFuture.sync(); Channel channel = channelFuture.channel(); Scanner scanner = new Scanner(System.in); // 创建一个线程用于输入并向服务器发送 new Thread(()->{ while (true) { String msg = scanner.next(); if ("q".equals(msg)) { // 关闭操作是异步的,在NIO线程中执行 channel.close(); break; } channel.writeAndFlush(msg); } }, "inputThread").start(); // 获得closeFuture对象 ChannelFuture closeFuture = channel.closeFuture(); System.out.println("waiting close..."); // 同步等待NIO线程执行完close操作 closeFuture.sync(); // 关闭之后执行一些操作,可以保证执行的操作一定是在channel关闭以后执行的 System.out.println("关闭之后执行一些额外操作..."); // 关闭EventLoopGroup group.shutdownGracefully(); } }
关闭channel时,可以调用channel.close()方法进行关闭。但是该方法也是一个异步方法。真正的关闭操作并不是在调用该方法的线程中执行的,而是在NIO线程中执行真正的关闭操作
如果想在channel真正关闭以后,执行一些额外的操作,可以选择以下两种方法来实现
通过channel.closeFuture()方法获得对应的ChannelFuture对象,然后调用sync()方法阻塞执行操作的线程,等待channel真正关闭后,再执行其他操作
// 获得closeFuture对象 ChannelFuture closeFuture = channel.closeFuture(); // 同步等待NIO线程执行完close操作 closeFuture.sync();
调用closeFuture.addListener方法,添加close的后续操作
closeFuture.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture channelFuture) throws Exception { // 等待channel关闭后才执行的操作 System.out.println("关闭之后执行一些额外操作..."); // 关闭EventLoopGroup group.shutdownGracefully(); } });
立志如山 静心求实
浙公网安备 33010602011771号