1、依赖
<!-- netty依赖-->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.0.23.Final</version>
</dependency>
2、搭建 Netty 服务端
Netty服务器:NettyServer
public class NettyServer {
public NettyServer(int port) {
this.port = port;
}
public static void main(String[] args) throws Exception {
new EchoServer(8888).start(); // 启动
}
public void start() {
// 负责连接请求
EventLoopGroup bossGroup = new NioEventLoopGroup();
// 负责事件响应
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// 服务器启动项
ServerBootstrap serverBootstrap = new ServerBootstrap();
// 临时存放已完成三次握手的请求的队列的最大长度。
// 如果未设置或所设置的值小于1,Java将使用默认值50。
// 如果大于队列的最大长度,请求会被拒绝
serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024);
// 保持长连接状态
serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
//绑定线程池:handler是针对bossGroup,childHandler是针对workerHandler
serverBootstrap.group(bossGroup, workerGroup)
// 选择nioChannel
.channel(NioServerSocketChannel.class)
// 绑定监听端口
.localAddress(this.port)
// 绑定客户端连接时候触发操作
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
logger.info("新增客户端连接,IP:" + ch.localAddress().getHostName() + ",Port:" + ch.localAddress().getPort());
ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(1024, 22, 2, 1, 0));
ch.pipeline().addLast(new ByteArrayEncoder());// 编码(发送数据)
ch.pipeline().addLast(new ByteArrayDecoder());// 解码(接受数据)
ch.pipeline().addLast(new NettyServerHandler()); // 客户端触发操作
}
});
// 服务器异步创建绑定定
ChannelFuture channelFuture = serverBootstrap.bind().sync();
//该方法进行阻塞,等待服务端链路关闭之后继续执行。
//这种模式一般都是使用Netty模块主动向服务端发送请求,然后最后结束才使用
channelFuture.channel().closeFuture().sync();
} finally {
// 释放线程池资源
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
Netty处理器:NettyServerHandler
@Component
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
// 是否登出操作
private Boolean isLogout;
//region 使用依赖注入
private static NettyServerHandler nettyServerHandler;
@Autowired
MyService myService;
public NettyServerHandler() {
}
@PostConstruct
public void init() {
nettyServerHandler = this;
}
//region
/**
* channel 通道 action 活跃
* 当客户端主动链接服务端的链接后,这个通道就是活跃的了。也就是客户端与服务端建立了通信通道并且可以传输数据
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
logger.info("客户端连接成功:" + ctx.channel().localAddress().toString() + " 通道已激活!");
}
/**
* channel 通道 action 不活跃
* 当客户端主动断开服务端的链接后,这个通道就是不活跃的。也就是说客户端与服务端的关闭了通信通道并且不可以传输数据
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
logger.info("客户端断开连接:" + ctx.channel().localAddress().toString() + " 通道不活跃!");
// channel失效,从Map中移除
NettyChannelMap.removeValue((SocketChannel) ctx.channel());
// 关闭流
ctx.close();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
logger.info("服务器:收到客户端数据");
try {
byte[] msgByte = (byte[]) msg;
// TODO 解析数据包
DataPackage dataPackage = nettyServerHandler.myService.baseAnalysis(msgByte);
// 唯一标识码
String uuid = dataPackage.getUuid();
// 将channel保存到内存
NettyChannelMap.add(uuid, (SocketChannel) channelHandlerContext.channelHandlerContext.channel());
// TODO 其他操作
nettyServerHandler.myService.analysis(msgByte);
//返回应答
NettyChannelMap.get(uuid).writeAndFlush(response_msg);
} finally {
ReferenceCountUtil.release(msg);
}
}
/**
* 读取完毕客户端发送过来的数据之后的操作
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
logger.info("服务端:接收数据处理完毕");
// 登出:写一个空的buf,并刷新写出区域。完成后关闭sock channel连接。
if (th