Files
MiddlePlatform/maibu-web-middleware/src/main/java/com/maibu/netty/NettyClient.java
2026-04-17 10:32:57 +08:00

223 lines
6.8 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.maibu.netty;
import com.fastbee.common.MiddleConstant;
import com.fastbee.netty.handler.*;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.TimeUnit;
/**
* Netty客户端实现类
* 提供与TCP服务器的连接管理、消息发送、重连机制和心跳检测功能
*/
public class NettyClient {
private static final Logger logger = LoggerFactory.getLogger(NettyClient.class);
// private static volatile NettyClient instance;
public String token;
public String userName;
public String requestDeviceId; //前端需要连接的设备
public String currentDeviceId; //当前连接的设备
public Long heartBeatTimer;
public String key; //userName + web + token
public String host;
public int port;
public int reconnectDelay = 5;
public int heartbeatInterval = 5;
public EventLoopGroup group;
public Bootstrap bootstrap;
public Channel channel;
public Long lastSwitchTime = -1L;
public Long Interval = 5000L;
public static final byte[] HEARTBEAT_PACKET = new byte[]{(byte) 0xAB, (byte) 0xAA, (byte) 0xFF, (byte) 0xAA, (byte) 0xAB};
// /**
// * 获取单例实例(无回调)
// */
// public static NettyClient getInstance() {
// if (instance == null) {
// synchronized (NettyClient.class) {
// if (instance == null) {
// instance = new NettyClient();
// }
// }
// }
// return instance;
// }
/**
* 初始化并连接到服务器
*/
public void initConnect(String host, int port) {
if (channel != null && channel.isActive()) {
logger.info("Netty客户端已连接,不重复初始化");
return;
}
this.host = host;
this.port = port;
connect();
}
/**
* 初始化Netty客户端
*/
public void initClient() {
group = new NioEventLoopGroup();
bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.SO_KEEPALIVE, true)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
// 设置共享属性
ch.attr(MiddleConstant.ATT_MASTER_KEY).set(key);
// 空闲检测处理器(心跳)
pipeline.addLast(new IdleStateHandler(0, heartbeatInterval, 0, TimeUnit.SECONDS));
pipeline.addLast(new MiddleHeaderFooterDecoder());
// 编解码器
pipeline.addLast(new MiddleHexDecoder());
pipeline.addLast(new MiddleHexEncoder());
// 自定义处理器
pipeline.addLast(new ClientHandler());
}
});
}
/**
* 连接到服务器
*/
public void connect() {
if (bootstrap == null || host == null || port <= 0) {
logger.error("Netty客户端未正确初始化");
return;
}
logger.info("正在连接到Netty服务器: {}:{}", host, port);
bootstrap.connect(host, port).addListener((ChannelFutureListener) future -> {
if (future.isSuccess()) {
channel = future.channel();
logger.info("Netty客户端连接成功: {}:{}", host, port);
// notifyConnectionStatus(true);
} else {
logger.error("Netty客户端连接失败,{}秒后重试: {}", reconnectDelay, future.cause().getMessage());
// notifyConnectionStatus(false);
// 重连
group.schedule(this::connect, reconnectDelay, TimeUnit.SECONDS);
}
});
}
/**
* 断开连接
*/
public void disconnect() {
if (channel != null) {
channel.close();
channel = null;
}
// notifyConnectionStatus(false);
logger.info("Netty客户端已断开连接");
}
/**
* 发送数据
*/
public void sendData(byte[] data) {
if (channel != null && channel.isActive()) {
channel.writeAndFlush(data).addListener((ChannelFutureListener) future -> {
if (future.isSuccess()) {
logger.debug("Netty客户端发送数据成功: {}", bytesToHex(data));
} else {
logger.error("Netty客户端发送数据失败: {}", future.cause().getMessage());
}
});
} else {
logger.error("Netty客户端未连接,发送数据失败");
}
}
/**
* 发送十六进制字符串数据
*/
public void sendHexData(String hexData) {
try {
byte[] data = hexStringToBytes(hexData);
sendData(data);
} catch (IllegalArgumentException e) {
logger.error("无效的十六进制字符串: {}", hexData);
}
}
/**
* 十六进制字符串转字节数组
*/
private static byte[] hexStringToBytes(String hexString) {
hexString = hexString.replaceAll("\\s+", "");
if (hexString.length() % 2 != 0) {
throw new IllegalArgumentException("十六进制字符串长度必须为偶数");
}
byte[] result = new byte[hexString.length() / 2];
for (int i = 0; i < hexString.length(); i += 2) {
result[i / 2] = (byte) Integer.parseInt(hexString.substring(i, i + 2), 16);
}
return result;
}
/**
* 字节数组转十六进制字符串
*/
private static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02X ", b));
}
return sb.toString().trim();
}
/**
* 检查客户端是否已连接
*/
public boolean isConnected() {
return channel != null && channel.isActive();
}
/**
* 设置重连延迟时间(秒)
*/
public void setReconnectDelay(int reconnectDelay) {
if (reconnectDelay > 0) {
this.reconnectDelay = reconnectDelay;
}
}
/**
* 设置心跳间隔时间(秒)
*/
public void setHeartbeatInterval(int heartbeatInterval) {
if (heartbeatInterval > 0) {
this.heartbeatInterval = heartbeatInterval;
}
}
}