前言
上一篇中简单介绍了 NIO ,也演示了一个 NIO 的Demo,我们可以看出 NIO 的编程相对来说比较麻烦,必须对 API 很熟悉才行,而 Netty 框架相对于原生的 NIO 来说它的API 使用是比较简单的,并且功能强大,内置了多种编解码功能,支持多种主流协议,修复了已经发现的所有JDK API BUG等。接下来我们用 Netty 框架来实现上一篇的 Demo。
Demo
- 环境
本文所使用的 Netty 版本 4.1.18.Final, 在 pom.xml 添加如下代码或者点击下载:
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.18.Final</version>
</dependency>
- 代码分析
一、服务端
(一)DemoServerHandler
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
/**
* Created by dzb on 2017/12/17.
* 该 Demo 只需要用到少量方法,所以继承 ChannelInboundHandlerAdapter 就可以了,
* 它提供了ChannelInboundHandler的默认实现
*/
public class DemoServerHandler extends ChannelInboundHandlerAdapter {
/**
* 每传入一次消息都要调用这个方法
* @param ctx
* @param msg
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
System.out.println("服务端收到客户端传来的消息是:" + buf.toString(CharsetUtil.UTF_8));
/**
* 将数据写到 Channel 中,等到操作结束再刷新发送出去
*/
ctx.write(Unpooled.copiedBuffer("Hello Client".getBytes()));
}
/**
* 当前操作的最后一条消息读取后调用,也就是说一次操作可能多条消息
* @param ctx
* @throws Exception
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
// 刷新发送数据
ctx.flush();
}
/**
* 读取操作期间发生异常调用的
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
// 关闭 Channel
ctx.close();
}
}
(二)DemoServer
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
/**
* Created by dzb on 2017/12/17.
*/
public class DemoServer{
private static int port = 8080;
public static void main(String[] args) {
// 创建 NIO 线程组,这里创建两个的原因是
// 一个用于服务端接收客户端的连接,另外一个用于进行 SocketChannel 的网络读写
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
try {
// NIO 服务端的辅助启动类
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workGroup)
// 设置Channel为NioServerSocketChannel,功能对应着NIO的ServerSocketChannel类
.channel(NioServerSocketChannel.class)
// 绑定处理类
.childHandler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new DemoServerHandler());
}
});
// 绑定端口, 同步等待成功
ChannelFuture f = b.bind(port).sync();
// 等待服务端监听端口关闭
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
// 释放所有资源
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
}
二、客户端
(一)DemoClientHandler
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;
/**
* Created by dzb on 2017/12/17.
*/
public class DemoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
/**
* 与服务器建立连接后调用
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
// 发送数据
ctx.writeAndFlush(Unpooled.copiedBuffer("Hello Server", CharsetUtil.UTF_8));
}
/**
* 接受到消息时调用
* @param ctx
* @param msg
* @throws Exception
*/
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
System.out.println("收到服务端的信息:" + msg.toString(CharsetUtil.UTF_8));
}
/**
* 发生异常时调用
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
ctx.close();
}
}
(二)DemoClient
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
/**
* Created by dzb on 2017/12/17.
*/
public class DemoClient {
private static int port = 8080;
private static String host = "127.0.0.1";
public static void main(String[] args) {
// 创建线程组
EventLoopGroup group = new NioEventLoopGroup();
// 创建客户端的启动
try {
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new DemoClientHandler());
}
});
// 发起异步连接操作
ChannelFuture f = b.connect(host, port).sync();
// 等待客户端链路关闭
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
group.shutdownGracefully();
}
}
}