一:简介
- Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。也就是说,Netty 是一个基于NIO的客户、服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户、服务端应用。Netty相当于简化和流线化了网络应用的编程开发过程,例如:基于TCP和UDP的socket服务开发。“快速”和“简单”并不用产生维护性或性能上的问题。Netty 是一个吸收了多种协议(包括FTP、SMTP、HTTP等各种二进制文本协议)的实现经验,并经过相当精心设计的项目。最终,Netty 成功的找到了一种方式,在保证易于开发的同时还保证了其应用的性能,稳定性和伸缩性
二:编码
- 引入程序包
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.36.Final</version>
</dependency>
- 搭建netty服务
@Slf4j
@Component
public class NettyServer implements ApplicationRunner {
/**
* @Author: guwenhai
* @Description: 开启服务线程
* @Date: 2021-03-27 15:40
*/
@Override
public void run(ApplicationArguments args) throws Exception {
String tcpIp = ""//tcpIp
String tcpPort = "" //tcp端口
start(new InetSocketAddress(tcpIp, Integer.valueOf(tcpPort)));
}
public void start(InetSocketAddress socketAddress) {
//new 一个主线程组
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
//new 一个工作线程组
EventLoopGroup workGroup = new NioEventLoopGroup(200);
ServerBootstrap bootstrap = new ServerBootstrap()
.group(bossGroup, workGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ServerChannelInitializer())
.localAddress(socketAddress)
//设置队列大小
.option(ChannelOption.SO_BACKLOG, 1024)
// 两小时内没有数据的通信时,TCP会自动发送一个活动探测数据报文
.childOption(ChannelOption.SO_KEEPALIVE, true);
//绑定端口,开始接收进来的连接
try {
ChannelFuture future = bootstrap.bind(socketAddress).sync();
log.info("Netty服务器启动开始监听端口: {}", socketAddress.getPort());
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
log.error("Netty端口冲突,异常信息:{}", e.getMessage());
e.printStackTrace();
} finally {
//关闭主线程组
bossGroup.shutdownGracefully();
//关闭工作线程组
workGroup.shutdownGracefully();
}
}
}
- netty服务初始化器
public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast("handler", new NettyServerHandler());
}
}
- netty服务端处理器
@Slf4j
@Component
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
//定义本类的静态对象
private static NettyServerHandler nettyServerHandler;
@PostConstruct
public void init(){
nettyServerHandler = this;
}
/**
* 客户端连接会触发
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
log.info("TCP对象线程: {}", ctx);
}
/**
* 客户端发消息会触发
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//客户端上传消息
String message = onMessage(msg);
}
/**
* 发生异常触发
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
close(ctx);
}
/**
* @Author: guwenhai
* @Description: 获取指定客户端的上传信息
* @Date: 2020/11/2 11:56
*/
public static String onMessage(Object msg) {
ByteBuf buf = (ByteBuf)msg;
byte [] bytes = new byte[buf.readableBytes()];
buf.readBytes(bytes);//复制内容到字节数组bytes
String message = HexEcodeUtil.ByteArrayToHexStr(bytes);//将接收到的数据转为字符串,此字符串就是客户端发送的字符串
return message;
}
/**
* @Author: guwenhai
* @Description: 公用回写数据到客户端的方法
* @param receiveStr 写入数据
* @param ctx 当前客户端
* @param mark 类型
* @Date: 2021-04-25 15:29
*/
public static void writeToClient(final String receiveStr, ChannelHandlerContext ctx,String mark) {
try {
ByteBuf bufff = Unpooled.buffer();//netty需要用ByteBuf传输
bufff.writeBytes(HexEcodeUtil.hexStrToBinaryStr(receiveStr));//对接需要16进制
ctx.writeAndFlush(bufff).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
StringBuilder sb = new StringBuilder("");
if(!StringUtils.isEmpty(mark)){
sb.append("【").append(mark).append("】");
}
if (future.isSuccess()) {
log.info("回写成功:" + sb.toString() + "{}",receiveStr);
} else {
log.info("回写失败:" + sb.toString() + "{}",receiveStr);
}
}
});
} catch (Exception e) {
e.printStackTrace();
log.error("调用通用writeToClient()异常:",e);
close(ctx);
}
}
/**
* @Author: guwenhai
* @Description: 指定netty资源回收
* @Date: 2020/11/2 11:57
*/
public static void close(ChannelHandlerContext ctx) {
if (ctx != null) {
try {
log.info("关闭netty:{}", ctx);
ctx.close();
} catch (Exception e) {
log.error("关闭netty异常{}", e);
}
}
}
}