Netty编程(三)—— Channel

Netty编程(三)—— Channel

Channel常用方法

  • close() 可以用来关闭Channel
  • closeFuture() 用来处理 Channel 的关闭事件
    • sync 方法作用是同步等待 Channel 关闭
    • 而 addListener 方法是异步等待 Channel 关闭
  • pipeline() 方法用于添加处理器
  • write() 方法将数据写入
    • 因为缓冲机制,数据被写入到 Channel 中的缓冲区以后,不会立即被发送
    • 只有当缓冲满了或者调用了flush()方法后,才会将数据通过 Channel 发送出去
  • writeAndFlush() 方法将数据写入并立即发送(刷出)

为什么需要sync()

在前两篇博客中《Netty编程(一)—— 初识Netty+超全注释》以及 《Netty编程(二)—— EventLoop》多次出现客户端部分的代码,而在其中有一句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方法,下面以一个例子来解释这个方法:

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方法执行完毕后,也就是连接真正建立后
        channelFuture.addListener(new ChannelFutureListener() {
        	//在nio线程连接建立好了以后,会调用
            @Override
            public void operationComplete(ChannelFuture channelFuture) throws Exception {
                Channel channel = channelFuture.channel();
                channel.writeAndFlush("hello world");
            }
        });
        System.in.read();
    }
}

当客户端与服务端成功建立连接后,会执行addListener方法中的operationComplete方法,其中他传入的参数channelFuture是调用addListener方法的channelFuture,之后执行其中的方法。值得注意的是,operationComplete方法是在NIO线程中执行的,即由事件循环组负责。

处理关闭

当我们要关闭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();
        }
    });
    
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值