Netty是业界最流行的NIO框架之一,它的健壮性、功能、性能和可扩展性在同类框架中都是首屈一指的,它已经得到了成百上千的商用项目的验证。本文是Netty网络编程的入门教程,从Netty开发环境的搭建到Netty入门实例编程。

Netty入门教程

  • 1. Netty开发环境搭建
    • 1.1. 下载Netty的jar包
    • 1.2 搭建Netty工程
    • 1.3 使用Maven工程
  • 2. Netty编程入门案例
    • 2.1 Netty服务端编程
    • 2.2 Netty客户端编程
    • 2.3 运行结果

1. Netty开发环境搭建

环境配置:

  • JDK1.8
  • IDEA 2020.1
  • Netty 4.0.56

由于Netty是一个基于Java的NIO编程框架,我们需要实现下载安装好JDK并且配置环境变量,在这里我就不介绍JDK的安装配置了,不懂的大家可以自行百度。关于编程所使用的工具,我使用的是IDEA,大家也可以选择其它的环境,下面简要介绍Netty开发环境搭建。

1.1. 下载Netty的jar包

访问Netty的官网http://netty.io/,从Downloads标签页选择下载安装,将压缩包下载完成之后,进行解压,目录结构如下图所示:


在这个里面我们可以找到各个模块的jar包和源码,由于我们直接以二进制类库的方式使用Netty,所以只需要获取netty-all-4.0.56.Final.jar即可,该jar包在all-in-one目录下。

1.2 搭建Netty工程

使用IDEA创建普通的Java工程,创建相应的package和类,如下图所示:

接下来我们在所建的java工程添加外部jar包,步骤为:File->Project Structure -> Libraries ->点击+ ->选择java ->选择netty-all-4.0.56.Final.jar添加即可,如下图所示:

1.3 使用Maven工程

除了使用上述方法直接建立java工程然后导入jar包之外,我们还可以使用Maven工程,添加netty相应的依赖即可,下面给出netty的maven依赖,具体的maven工程构建可以百度。

  <dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty</artifactId> <!-- Use 'netty-all' for 4.0 or above -->
    <version>X.Y.Z.Q</version>
    <scope>compile</scope>
  </dependency>

2. Netty编程入门案例

本节我们将使用一个简单的时间服务器的例子来演示如何使用Netty进行网络编程。下面分服务端和客户端分别进行介绍。

2.1 Netty服务端编程

下面首先给出代码,然后再进行相应的解释,具体代码如下所示。

package netty.base;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
/**
 * created by LMR on 2020/5/20
 */
public class TimeServer {
    public static void main(String[] args) throws Exception{
        int port = 8080;
        new TimeServer().bind(port);
    }

    public void bind(int port) throws Exception{
        //配置服务端NIO线程组
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childHandler(new ChildChannelHandler());
            //绑定端口,同步等待成功
            ChannelFuture f = serverBootstrap.bind(port).sync();
            //等待服务端监听端口关闭
            f.channel().closeFuture().sync();
        }finally {
            //优雅退出,释放资源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
    private class ChildChannelHandler extends ChannelInitializer<SocketChannel>{
        @Override
        protected void initChannel(SocketChannel socketChannel) throws Exception {
            socketChannel.pipeline().addLast(new TimeServerHandler());
        }
    }
}

在bind方法中,首先创建了两个NioEventLoopGroup实例。NioEventLoopGroup本子上是个线程组,专门用于网络事件的处理,与Java的NIO编程中的Reactor类似。这里的两个实例,一个用于服务端接受客户端连接,另一个用于进行SocketChannel的网络读写。之后创建ServerBootstrao对象,器用于启动NIO服务端,主要是为了降低服务端的开发难度。接下来调用ServerBootstrap的group方法,将两个NIO线程组当作参数传入。然后设置创建的Channel为NioServerSocketChannel,并且配置它的TCP参数,最后绑定I/O时间的处理类ChildChannelhandler。

配置完成之后,就会调用ServerBootstrap的bind方法绑定监听端口,并且调用它的同步阻塞方法sync等待绑定按操作完成。完成之后会返回一个ChannelFuture ,主要用于异步操作的通知回调。接下来看看TimeServerHandler类是如何实现的。

package netty.base;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

/**
 * created by LMR on 2020/5/20
 */
public class TimeServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg)
            throws Exception {
        //将消息转为buf
        ByteBuf buf=(ByteBuf) msg;
        //创建一个buf长度的数组
        byte [] requestbyte=new byte[buf.readableBytes()];
        //将缓冲区的数据写入到字节数组
        buf.readBytes(requestbyte);
        //根据字节数组创建字符串
        String request=new String(requestbyte,"utf-8");
        System.out.println("The time server receive order : " + request);
        String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(request) ? new
                java.util.Date(System.currentTimeMillis()).toString() : "BAD ORDER";
        //响应给客户端
        ByteBuf resBuf= Unpooled.copiedBuffer(currentTime.getBytes());
        ctx.write(resBuf)	;
    }
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
    //刷新数据,将数据从缓冲区刷新到Channel中去
        ctx.flush();
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        //当异常发生时,关闭ChannelHandlerContext,释放和ChannelHandlerContext相关联的句柄等资源
        ctx.close();
    }
}

TimeServerHandler继承ChannelInboundHandlerAdapter ,它用于对网络事件进行读写操作,通常我们只需要关注channelRead和exceptionCaught方法。关于这两个方法中的代码,下上面已经有了详细的注释,在这里就不再进行介绍。接下来介绍客户端的实现代码。

2.2 Netty客户端编程

Netty客户端的开发相比于服务端更简单,下面来看具体的实现代码。

package netty.base;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
/**
 * created by LMR on 2020/5/20
 */
public class TimeClient {

    public static void main(String[] args) throws Exception{
        int port = 8080;
        new TimeClient().connect("127.0.0.1", port);
    }
    public void connect(String host, int port) throws Exception{
        NioEventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group).channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new TimeClientHandler());
                        }
                    });
            //发起异步连接操作
            ChannelFuture f = bootstrap.connect(host, port).sync();
            //等待客户端连接关闭
            f.channel().closeFuture().sync();
        }finally {
            group.shutdownGracefully();
        }
    }
}

首先在connect方法中创建客户端处理I/O读写的NioEventLoopGroup线程组,然后继续创建客户端辅助启动类Boostrap,随后对其进行配置 。与服务端不同点在于,客户端的Channel需要设置为NioSocketChannel类型,然后为其添加handler,这里我们使用了匿名内部类(不懂得自行百度或者留言),实现initChannel方法,其作用是当创建NioSocketChannel成功之后,在初始化它的时候将它的ChannelHandler设置到ChannelPipeline中,用于处理网络I/O事件。

下面我们看一下TimeClientHandler的代码实现。

package netty.base;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
/**
 * created by LMR on 2020/5/20
 */
public class TimeClientHandler extends ChannelInboundHandlerAdapter {

    private final ByteBuf message;
   public TimeClientHandler(){
   //创建发送消息的字节数组
       byte[] req = "QUERY TIME ORDER".getBytes();
       //将字节数组写入缓冲区
       message = Unpooled.buffer(req.length);
       message.writeBytes(req);
   }
   //当客户端和服务端TCP链路建立成功之后会调用此方法
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
    //将消息从缓冲区刷新到Channel
        ctx.writeAndFlush(message);
    }
        //当客户端接受到服务端传来的消息时执行
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

       //处理服务端发送过来的消息
        ByteBuf buf = (ByteBuf) msg;
        byte[] req = new byte[buf.readableBytes()];
        buf.readBytes(req);
        String body = new String(req, "UTF-8");
        System.out.println("Now is : " + body);
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
       ctx.close();
    }
}

在客户端处理类中需要注意的就是上述代码中重写的三个方法,关于方法的调用场景和实现代码都进行了详细的注释,在这里就不再解释。

到这里,我们就完成了Netty的服务端和客户端的编写,我们可以看出,使用Netty进行NIO网络编程要远比Java原生的NIO编程简单。

2.3 运行结果

服务端运行结果:

客户端运行结果:

写在后面的话:

本书中的案例来自于《Netty权威指南》,但由于Netty版本不同,具体的实现代码与原书有所不同。

如果喜欢的话希望点赞收藏,关注我,将不间断更新博客。

希望热爱技术的小伙伴私聊,一起学习进步

来自于热爱编程的小白

更多推荐

还在为Java的NIO编程发愁吗?也许你该接触下Netty网络编程了(从Netty环境搭建到Netty入门案例)