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入门案例)
发布评论