Netty之ChannelFuture详解

目录

目标

Netty版本

Netty官方API

客户端如何与服务器建立连接&连接成功后的操作方式

实现

如何处理客户端与服务器连接关闭后的操作

正确关闭连接的方式

方法一

方法二


目标

了解Netty如何处理客户端与服务器之间的连接与关闭问题。


Netty版本

        <dependency>
			<groupId>io.netty</groupId>
			<artifactId>netty-all</artifactId>
			<version>4.1.87.Final</version>
		</dependency>

Netty官方API

Netty API Reference (4.1.87.Final)https://netty.io/4.1/api/index.html


客户端如何与服务器建立连接&连接成功后的操作方式

Bootstrap的connect方法是实现了客户端与服务器之间的连接。需要明确一点,客户端与服务器之间的连接不是main线程在做,而是由我们指定的EventLoop来做。如此看来,connect方法是异步非阻塞的,如果连接还没有建立好就无法获取Channel,因此,ChannelFuture有2种方法处理连接建立后的操作,其中:

  1. ChannelFuture的sync()方法,作用是阻塞main线程,等到连接建立好程序才向下运行。Channel由主线程获取。
  2. ChannelFuture用addListener()方法。异步调用回调对象的operationComplete方法。Channel由我们指定的EventLoop线程获取,本质是把ChannelFutureListener对象传递给EventLoopGroup线程,EventLoopGroup线程建立好连接以后调用operationComplete方法。

实现

package com.ctx.netty;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringEncoder;
import lombok.extern.slf4j.Slf4j;

import java.nio.charset.Charset;
@Slf4j
public class NettyClient {
    /**
     * 建立连接后的操作:
     * 方法一:用sync()方法。
     */
    public void fun(ChannelFuture channelFuture) throws InterruptedException {
        /**
         * 如果连接还没有建立好就无法获取Channel,因此,有了sync()方法,
         * 作用是阻塞等到连接建立好程序才向下运行。
         */
        channelFuture.sync();
        Channel channel = channelFuture.channel();
        //打印main线程
        log.info("channel==========={}",channel);
        //向服务器发送数据
        channel.writeAndFlush("Hello world!");
    }
    /**
     * 建立连接后的操作:
     * 方法二:用addListener()方法。异步调用回调对象的operationComplete方法。
     */
    public void fun2(ChannelFuture channelFuture) throws InterruptedException {
        //把ChannelFutureListener对象传递给EventLoopGroup线程,EventLoopGroup线程建立好连接以后调用operationComplete方法。
        channelFuture.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture channelFuture) throws Exception {
                Channel channel = channelFuture.channel();
                //打印nioEventLoopGroup线程
                log.info("channel==========={}",channel);
                //向服务器发送数据
                channel.writeAndFlush("Hello world!");
            }
        });
    }

    public static void main(String[] args) throws InterruptedException {
        //启动Netty客户端
        Bootstrap bootstrap = new Bootstrap();
        //选择EventLoop
        bootstrap.group(new NioEventLoopGroup());
        //选择客户端Channel实现,Channel是数据的传输通道。
        bootstrap.channel(NioSocketChannel.class);
        //添加处理器,
        bootstrap.handler(new ChannelInitializer<NioSocketChannel>() {
            //连接建立后初始化Channel
            @Override
            protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
                //把字符串编码成ByteBuf
                nioSocketChannel.pipeline().addLast(new StringEncoder(Charset.forName("UTF-8")));
            }
        });
        /**
         * Netty客户端和服务端建立连接是由EventLoopGroup线程负责的,而不是由main线程负责的。
         * 因此,这个建立连接的过程是异步非阻塞操作。
         */
        ChannelFuture channelFuture = bootstrap.connect("localhost", 8999);

        //new NettyClient().fun( channelFuture);

        new NettyClient().fun2( channelFuture);
    }
}

如何处理客户端与服务器连接关闭后的操作

Channel的close方法用来关闭客户端与服务器之间地连接,但是close方法是个异步方法,如果需要关闭连接才处理后续的业务则要考虑不同线程的执行顺序问题。

正确关闭连接的方式

方法一

package com.zteict.trans.test;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;

import java.nio.charset.Charset;

public class RespClient {
    public static void main(String[] args) throws InterruptedException {
        NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup();
        try {
            //启动Netty客户端
            Bootstrap bootstrap = new Bootstrap();
            //选择EventLoop
            bootstrap.group(nioEventLoopGroup);
            //选择客户端Channel实现,Channel是数据的传输通道。
            bootstrap.channel(NioSocketChannel.class);
            //添加处理器,
            bootstrap.handler(new ChannelInitializer<NioSocketChannel>() {
                //连接建立后初始化Channel
                @Override
                protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
                    nioSocketChannel.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                        @Override
                        public void channelActive(ChannelHandlerContext ctx) {
                        }

                        //接收响应
                        @Override
                        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                        }
                    });
                }
            });
            ChannelFuture channelFuture = bootstrap.connect("172.19.216.135", 6379).sync();
            channelFuture.channel().close();
            channelFuture.channel().closeFuture().sync();
        }finally{
            System.out.println("连接关闭了。");
            nioEventLoopGroup.shutdownGracefully();
        }
    }
}

方法二

package com.ctx.netty;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringEncoder;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class NettyClient {
    public static void main(String[] args) throws InterruptedException {
        NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup();
        //启动Netty客户端
        Bootstrap bootstrap = new Bootstrap();
        //选择EventLoop
        bootstrap.group(nioEventLoopGroup);
        //选择客户端Channel实现,Channel是数据的传输通道。
        bootstrap.channel(NioSocketChannel.class);
        //添加处理器,
        bootstrap.handler(new ChannelInitializer<NioSocketChannel>() {
            //连接建立后初始化Channel
            @Override
            protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
                //把字符串编码成ByteBuf
                nioSocketChannel.pipeline().addLast(new StringEncoder());
            }
        });
        ChannelFuture channelFuture = bootstrap.connect("localhost", 8999);
        channelFuture.sync();
        Channel channel = channelFuture.channel();
        log.debug("{}"+channel);
        channel.writeAndFlush("Hello World!") ;
        //close()是异步方法,如果该方法下面没有阻塞,则无法保证线程的执行顺序。
        channel.close();
        channelFuture= channel.closeFuture();
        //关闭连接的那个线程负责执行该方法。
        channelFuture.addListener(new ChannelFutureListener() {
            //关闭连接的那个线程负责执行该方法。
            @Override
            public void operationComplete(ChannelFuture channelFuture) throws Exception {
                log.info("连接关闭了。");
                /**
                 * 优雅地关闭nioEventLoopGroup,即:
                 * 拒绝接收新的任务,完成现有的任务。
                 */
                nioEventLoopGroup.shutdownGracefully();
            }
        });
    }
}

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值