fix(iot): TCP消息处理容错改进,避免异常断开连接
- 消息格式无法识别时记录警告并跳过,不断开连接 - 消息解码失败时记录警告并跳过,不断开连接 - 设备不存在时断开连接,其他认证失败只记录日志 - 添加 bytesToHex 工具方法用于调试 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -112,17 +112,30 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> {
|
||||
|
||||
// 2. 获取消息格式类型
|
||||
String codecType = getMessageCodecType(buffer, socket);
|
||||
if (codecType == null) {
|
||||
// 无法识别消息格式,记录警告但不断开连接
|
||||
byte[] data = buffer.getBytes();
|
||||
String preview = bytesToHex(data, Math.min(32, data.length));
|
||||
log.warn("[processMessage][无法识别消息格式,跳过该消息,clientId: {}, 数据前32字节: {}]",
|
||||
clientId, preview);
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 解码消息
|
||||
IotDeviceMessage message;
|
||||
try {
|
||||
message = deviceMessageService.decodeDeviceMessage(buffer.getBytes(), codecType);
|
||||
if (message == null) {
|
||||
throw new Exception("解码后消息为空");
|
||||
// 解码失败(如消息格式错误),记录警告但不断开连接
|
||||
log.warn("[processMessage][消息解码失败,跳过该消息,clientId: {}, codecType: {}]",
|
||||
clientId, codecType);
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 消息格式错误时抛出异常,由上层处理连接断开
|
||||
throw new Exception("消息解码失败: " + e.getMessage(), e);
|
||||
// 其他异常也记录警告但不断开连接
|
||||
log.warn("[processMessage][消息解码异常,跳过该消息,clientId: {}, codecType: {}, 错误: {}]",
|
||||
clientId, codecType, e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. 查找协议处理器
|
||||
@@ -147,7 +160,7 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> {
|
||||
* 认证结果处理:
|
||||
* - SUCCESS:注册连接,发送上线消息
|
||||
* - PENDING:等待后续认证步骤(如 JT808 注册后等待鉴权)
|
||||
* - FAILURE:认证失败,不做处理(协议处理器已发送失败响应)
|
||||
* - FAILURE:认证失败,如果设备不存在则断开连接,否则只记录日志
|
||||
*
|
||||
* @param clientId 客户端 ID
|
||||
* @param message 认证消息
|
||||
@@ -178,9 +191,16 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> {
|
||||
handler.getProtocolType(), result.getMessage());
|
||||
|
||||
} else {
|
||||
// 认证失败:协议处理器已发送失败响应,这里只记录日志
|
||||
// 认证失败
|
||||
String failureReason = result.getMessage();
|
||||
log.warn("[handleAuthentication][认证失败,clientId: {}, 协议: {}, 原因: {}]",
|
||||
clientId, handler.getProtocolType(), result.getMessage());
|
||||
clientId, handler.getProtocolType(), failureReason);
|
||||
|
||||
// 如果设备不存在,断开连接
|
||||
if (failureReason != null && failureReason.contains("设备不存在")) {
|
||||
log.warn("[handleAuthentication][设备不存在,断开连接,clientId: {}]", clientId);
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
@@ -373,7 +393,7 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> {
|
||||
*/
|
||||
private void cleanupConnection(NetSocket socket) {
|
||||
try {
|
||||
// 1. 发送离线消息(如果已认证)
|
||||
// 1. 发送离线消息(如果<EFBFBD><EFBFBD><EFBFBD>认证)
|
||||
IotTcpConnectionManager.ConnectionInfo connectionInfo = connectionManager.getConnectionInfo(socket);
|
||||
if (connectionInfo != null && connectionInfo.isAuthenticated()) {
|
||||
IotDeviceMessage offlineMessage = IotDeviceMessage.buildStateOffline();
|
||||
@@ -391,4 +411,26 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 字节数组转十六进制字符串
|
||||
*
|
||||
* @param bytes 字节数组
|
||||
* @param limit 最大长度
|
||||
* @return 十六进制字符串
|
||||
*/
|
||||
private String bytesToHex(byte[] bytes, int limit) {
|
||||
if (bytes == null || bytes.length == 0) {
|
||||
return "";
|
||||
}
|
||||
int length = Math.min(bytes.length, limit);
|
||||
StringBuilder sb = new StringBuilder(length * 2);
|
||||
for (int i = 0; i < length; i++) {
|
||||
sb.append(String.format("%02X", bytes[i]));
|
||||
}
|
||||
if (bytes.length > limit) {
|
||||
sb.append("...");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user