**
搭建Netty的一个简单应用
**
1、添加netty的依赖
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.0.36.Final</version>
</dependency>
2、服务端
/**
* Netty服务端.
*
* @author Mrx
* @since 2021/1/27
*/
public class NettyServer {
/**
* 设置服务端端口
*/
private static final int PORT = 6666;
/*
* 通过nio方式来接收连接和处理连接
* EventLoopGroup是用来处理IO操作的多线程事件循环器
*/
/**
* bossGroup 用来接收进来的连接(并没有处理请求)
*/
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
/**
* workerGroup 用来处理已经被接收的连接
*/
NioEventLoopGroup workGroup = new NioEventLoopGroup();
/**
* 开启服务线程,netty通过ServerBootstrap启动服务端。
* Netty创建全部都是实现自AbstractBootstrap。
* 客户端的是Bootstrap,服务端的则是ServerBootstrap。
**/
public void start() {
try {
//启动 NIO 服务的辅助启动类
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workGroup)
//配置 Channel (异步的服务器端 TCP Socket 连接)
.channel(NioServerSocketChannel.class)
//服务端过滤器
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel socketChannel) throws Exception {
// 服务端业务逻辑
socketChannel.pipeline().addLast(new NettyServerHandler());
// 解决拆包问题
socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024));
// 获取数据的结果为string类型,解码和编码,应和客户端一致
socketChannel.pipeline().addLast(new StringEncoder());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
// 绑定端口,开始接收进来的连接
ChannelFuture f = serverBootstrap.bind(PORT).sync();
System.out.println("通讯服务启动成功:" + PORT);
// 等待服务器 socket 关闭。
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
System.err.println("通讯服务启动失败:" + PORT);
} finally {
//关闭EventLoopGroup,释放掉所有资源包括创建的线程
try {
bossGroup.shutdownGracefully().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
workGroup.shutdownGracefully().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
NettyServer nettyServer = new NettyServer();
nettyServer.start();
}
}
3、服务端处理器(处理业务逻辑)
/**
* 服务端请求处理Handler.
*
* @author Mrx
* @since 2021/1/27
*/
public class NettyServerHandler extends SimpleChannelInboundHandler<ByteBuf> {
/**
* 接受消息,返回信息
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) {
// 收到消息直接打印输出
System.out.println("服务端接受的消息: " + msg);
//另外一种方式,直接把ByteBuf转换成utf-8编码的字符串,输出也是一样
// String result = msg.toString(CharsetUtil.UTF_8);
byte[] result = new byte[msg.readableBytes()];
// msg中存储的是ByteBuf类型的数据,把数据读取到byte[]中
msg.readBytes(result);
String string= new String(result);
System.out.println("Client said:" + string);
// // 释放资源,这行很关键(添加这行会抛出异常IllegalReferenceCountException: refCnt: 0, decrement: 1)
// msg.release();
// 向客户端发送消息
String response = "hello client! take chenghua avenue!";
// 在当前场景下,发送的数据须转换成ByteBuf数组
ByteBuf buff = ctx.alloc().buffer(4 * response.length());
buff.writeBytes(response.getBytes());
ctx.writeAndFlush(buff);
// ctx.writeAndFlush(Unpooled.copiedBuffer(response, CharsetUtil.UTF_8));
}
}
4、客户端
/**
* netty客户端.
*
* @author Mrx
* @since 2021/1/27
*/
public class NettyClient {
/**
* ip地址
*/
public static final String HOST = "127.0.0.1";
/**
* 端口
*/
public static final int PORT = 6666;
/*
* 通过nio方式来接收连接和处理连接
* EventLoopGroup是用来处理IO操作的多线程事件循环器
*/
/**
* workerGroup 用来处理已经被接收的连接
*/
NioEventLoopGroup workGroup = new NioEventLoopGroup();
public void connect(String host, int port) throws Exception {
try {
Bootstrap b = new Bootstrap();
b.group(workGroup);
//配置Channel (异步的客户端 TCP Socket 连接)
b.channel(NioSocketChannel.class);
b.option(ChannelOption.SO_KEEPALIVE, true);
b.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel socketChannel) throws Exception {
// 客户端业务逻辑
socketChannel.pipeline().addLast(new NettyClientHandler());
// 解决拆包问题
socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024));
// 获取数据的结果为string类型, 解码和编码,应和服务端一致
socketChannel.pipeline().addLast(new StringEncoder());
}
});
// Start the client.
ChannelFuture future = b.connect(host, port).sync();
//消息在这里发送,处理器那边接受服务端返回的消息
String str="Hello Server! to erxian bridge";
future.channel().writeAndFlush(str);
System.out.println("客户端发送数据:"+str);
// Wait until the connection is closed.
future.channel().closeFuture().sync();
} finally {
//关闭EventLoopGroup,释放掉所有资源包括创建的线程
try {
workGroup.shutdownGracefully().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws Exception {
NettyClient client = new NettyClient();
client.connect(HOST, PORT);
}
}
5、客户端处理器
/**
* 客户端端请求处理Handler..
*
* @author Mrx
* @since 2021/1/27
*/
public class NettyClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
System.out.println("客户端接受的消息: " + msg);
byte[] result = new byte[msg.readableBytes()];
msg.readBytes(result);
System.out.println("Server said:" + new String(result));
//添加这行会抛出异常IllegalReferenceCountException: refCnt: 0, decrement: 1
// result.release();
super.channelActive(ctx);
System.out.println("连接关闭! ");
super.channelInactive(ctx);
}
}
现在来启动一下服务端和客户端
服务端:
客户端:
用网络调试助手测试一下:
注:关于netty的原理、核心组件以及、服务器在处理响应的设计模式以及与传统网络编程的区别等可查看《Netty权威指南》