— 项目的部分代码,仅供参考
1.maven
io.netty
netty-all
4.1.59.Final
2.编辑NettyServer.java
public class NettyServer {
private static final Logger logger = LoggerFactory.getLogger("-----NettyServer-----");
private EventLoopGroup bossGroup = null;
private EventLoopGroup workerGroup = null;
public NettyServer() {
bossGroup = new NioEventLoopGroup(1);
workerGroup = new NioEventLoopGroup();
}
/**
* 启动并绑定 端口
*/
public void bind(int tcp, int socket) throws Exception {
ServerBootstrap device = new ServerBootstrap();
device.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
//初始化服务端可连接队列,指定了队列的大小128
.option(ChannelOption.SO_BACKLOG, 1024)
//保持长连接
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childOption(ChannelOption.RCVBUF_ALLOCATOR, new FixedRecvByteBufAllocator(65535))
.childOption(ChannelOption.TCP_NODELAY, true)
.childOption(ChannelOption.SO_REUSEADDR, true)
// 绑定客户端连接时候触发操作
.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel sh) throws Exception {
InetSocketAddress address = sh.remoteAddress();
logger.debug("TCP 客户端IP:" + address.getAddress() + ":" + address.getPort());
sh.pipeline()
.addLast(new FixedLengthFrameDecoder(10))
.addLast("HeartBeat", new HeartBeatHandler(redisUtil, handlerService));
}
});
//绑定监听端口,调用sync同步阻塞方法等待绑定操作完成,完成后返回ChannelFuture类似于JDK中Future
ServerBootstrap webSocket = new ServerBootstrap();
webSocket.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
//初始化服务端可连接队列,指定了队列的大小128
.option(ChannelOption.SO_BACKLOG, 1024)
//保持长连接
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childOption(ChannelOption.RCVBUF_ALLOCATOR, new FixedRecvByteBufAllocator(65535))
.childOption(ChannelOption.TCP_NODELAY, true)
.childOption(ChannelOption.SO_REUSEADDR, true)
// 绑定客户端连接时候触发操作
.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel sh) throws Exception {
InetSocketAddress address = sh.remoteAddress();
logger.debug("WEB SOCKET客户端IP:" + address.getAddress() + ":" + address.getPort());
sh.pipeline()
.addLast(new HttpServerCodec())
.addLast(new ChunkedWriteHandler())
.addLast(new HttpObjectAggregator(65535))
.addLast(new WebSocketServerProtocolHandler("/ws", "WebSocket", true, 65535))
.addLast(new WebSocketHandler());
}
});
//绑定监听端口,调用sync同步阻塞方法等待绑定操作完成,完成后返回ChannelFuture类似于JDK中Future
ChannelFuture futureDevice = device.bind(tcp).sync();
ChannelFuture futureWebSocket = webSocket.bind(socket).sync();
if (futureDevice.isSuccess()) {
logger.debug("TCP 服务端启动成功");
} else {
logger.debug("TCP 服务端启动失败");
futureDevice.cause().printStackTrace();
bossGroup.shutdownGracefully(); //关闭线程组
workerGroup.shutdownGracefully();
}
if (futureWebSocket.isSuccess()) {
logger.debug("WEB-SOCKET服务端启动成功");
} else {
logger.debug("WEB-SOCKET服务端启动失败");
futureWebSocket.cause().printStackTrace();
bossGroup.shutdownGracefully(); //关闭线程组
workerGroup.shutdownGracefully();
}
//成功绑定到端口之后,给channel增加一个 管道关闭的监听器并同步阻塞,直到channel关闭,线程才会往下执行,结束进程。
futureDevice.channel().closeFuture().sync();
futureWebSocket.channel().closeFuture().sync();
}
/**
* 端口解绑
*/
public void unbind() {
if (null != bossGroup && !bossGroup.isShutdown()) {
bossGroup.shutdownGracefully();
bossGroup = null;
}
if (null != workerGroup && !workerGroup.isShutdown()) {
workerGroup.shutdownGracefully();
workerGroup = null;
}
}
/**
* 十六进制数值 转十六进制字符串
*
* @param bytes byte数组
* @return
*/
public static String bytesToHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte aByte : bytes) {
String hex = Integer.toHexString(0xFF & aByte);
if (hex.length() == 1) {
sb.append('0');
}
sb.append(hex);
}
String result = sb.toString().toUpperCase();
return result;
}
}
3.HeartBeatHandler.java modbus协议数据处理
/**
* @author deilsky
* @date 2021/3/10 14:32
* @description 接收并处理设备报文
*/
@ChannelHandler.Sharable
public class HeartBeatHandler extends ChannelInboundHandlerAdapter {
private static final Logger logger = LoggerFactory.getLogger("-----HeartBeatHandler-----");
public HeartBeatHandler() {
}
/**
* 接收报文
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf byteBuf = (ByteBuf) msg;
int len = byteBuf.readableBytes();
logger.info("---------------start process msg--------------------");
logger.info("readable bytes is:" + len);
byte[] old = new byte[len];
for (int i = 0; i < len; i++) {
old[i] = byteBuf.readByte();
}
String message = NettyServer.bytesToHexString(old);
logger.info(String.format("message:%s", message));
//相关处理
}
public static String toBinaryString(String str) {
if (str == null) {
return null;
}
StringBuffer sb = new StringBuffer();
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
System.out.println(bytes.length);
for (byte aByte : bytes) {
sb.append(Integer.toBinaryString(aByte));
}
return sb.toString();
}
/**
* 接收到客户端信息完成
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
logger.info("接收到客户端信息完成");
ctx.flush();
}
/**
* 异常信息
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (cause instanceof Exception) {
logger.info("异常捕获");
cause.printStackTrace();
}
}
/**
* 接入连接
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
logger.info("CLIENT" + getRemoteAddress(ctx) + " 接入连接");
}
/**
* 断开连接
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
logger.info("CLIENT" + getRemoteAddress(ctx) + " 断开连接");
ctx.close();
}
/**
* 获取设备IP地址
*/
public static String getRemoteAddress(ChannelHandlerContext ctx) {
return ctx.channel().remoteAddress().toString();
}
}
4.WebSocketHandler.java 处理web-socket
private static final Logger logger = LoggerFactory.getLogger(WebSocketHandler.class);
/**
* 建立连接
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
channel.id();
logger.info("与客户端建立连接,通道开启!channelId:{}",channel.id());
}
/**
* 客户端与服务器关闭连接的时候触发
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
logger.info("与客户端建立连接,通道关闭!");
}
/**
* 服务器接受客户端的数据信息
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
logger.info("服务器收到的数据:" + msg.text());
}
/**
* 给固定的人发消息
*/
private void sendMessage(ChannelHandlerContext ctx) {
logger.info("服务器回复:0");
ctx.channel().writeAndFlush(new TextWebSocketFrame("0")).addListener((ChannelFutureListener) future -> {
logger.info("WEB-SOCKET 心跳回复:0");
logger.info("WEB SOCKET DONE:{}",future.isDone());
logger.info("WEB SOCKET SUCCESS:{}",future.isSuccess());
});;
}
}
5.使用方式
在springBoot 启动入口类中 加入如下代码:
放在main方法下面;
@Component
public static class StartApplication implements ApplicationRunner {
private NettyServer nettyServer;
@Value("${device.port1}")
private int port1;
@Value("${device.port2}")
private int port2;
/**
* 初始化数据
*/
@Override
public void run(ApplicationArguments args) throws Exception {
nettyServer = new NettyServer();
nettyServer.bind(port1,port2);
}
/**
* 程序关闭监听
* @throws Exception
*/
@PreDestroy
public void destroy() throws Exception {
logger.info("进程关闭!");
nettyServer.unbind();
}
}