废弃 ChannelManager 类,改用 SocketConnectionManager 处理 Socket 连接,优化指令发送和在线状态检查逻辑
This commit is contained in:
parent
25b11bbe04
commit
1a7aa385f1
|
|
@ -9,6 +9,8 @@ import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||||
import com.bonus.sgzb.material.utils.JTT808MessageProcessor;
|
import com.bonus.sgzb.material.utils.JTT808MessageProcessor;
|
||||||
|
import com.bonus.sgzb.material.utils.SocketConnectionManager;
|
||||||
|
import com.bonus.sgzb.material.service.IJtt808DataService;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import javax.annotation.PreDestroy;
|
import javax.annotation.PreDestroy;
|
||||||
|
|
@ -40,6 +42,12 @@ public class JTT808ServerConfig {
|
||||||
@Autowired
|
@Autowired
|
||||||
private JTT808MessageProcessor messageProcessor;
|
private JTT808MessageProcessor messageProcessor;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SocketConnectionManager socketConnectionManager;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IJtt808DataService jtt808DataService;
|
||||||
|
|
||||||
private ServerSocket serverSocket;
|
private ServerSocket serverSocket;
|
||||||
private final AtomicBoolean running = new AtomicBoolean(false);
|
private final AtomicBoolean running = new AtomicBoolean(false);
|
||||||
private final AtomicInteger activeConnections = new AtomicInteger(0);
|
private final AtomicInteger activeConnections = new AtomicInteger(0);
|
||||||
|
|
@ -177,6 +185,7 @@ public class JTT808ServerConfig {
|
||||||
private final Socket clientSocket;
|
private final Socket clientSocket;
|
||||||
private final int connectionId;
|
private final int connectionId;
|
||||||
private final Logger logger = LoggerFactory.getLogger(ClientHandler.class);
|
private final Logger logger = LoggerFactory.getLogger(ClientHandler.class);
|
||||||
|
private String terminalPhoneNumber = null; // 记录终端手机号
|
||||||
|
|
||||||
public ClientHandler(Socket clientSocket, int connectionId) {
|
public ClientHandler(Socket clientSocket, int connectionId) {
|
||||||
this.clientSocket = clientSocket;
|
this.clientSocket = clientSocket;
|
||||||
|
|
@ -211,6 +220,23 @@ public class JTT808ServerConfig {
|
||||||
// 减少活跃连接计数
|
// 减少活跃连接计数
|
||||||
int remaining = activeConnections.decrementAndGet();
|
int remaining = activeConnections.decrementAndGet();
|
||||||
|
|
||||||
|
// ✅ 从连接管理器中移除连接
|
||||||
|
String phoneNumber = socketConnectionManager.getPhoneNumberBySocket(clientSocket);
|
||||||
|
if (phoneNumber != null) {
|
||||||
|
boolean removed = socketConnectionManager.removeConnection(phoneNumber);
|
||||||
|
if (removed) {
|
||||||
|
logger.info("✅ 终端 {} 连接已从连接管理器移除", phoneNumber);
|
||||||
|
|
||||||
|
// 更新数据库中的离线状态
|
||||||
|
try {
|
||||||
|
jtt808DataService.updateTerminalOnlineStatus(phoneNumber, false, clientAddress);
|
||||||
|
logger.info("✅ 终端 {} 离线状态已更新到数据库", phoneNumber);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("更新终端离线状态异常: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
clientSocket.close();
|
clientSocket.close();
|
||||||
logger.info("连接 #{} 已关闭 (剩余活跃连接: {}): {}", connectionId, remaining, clientAddress);
|
logger.info("连接 #{} 已关闭 (剩余活跃连接: {}): {}", connectionId, remaining, clientAddress);
|
||||||
|
|
@ -236,7 +262,7 @@ public class JTT808ServerConfig {
|
||||||
|
|
||||||
// 使用消息处理器解析和处理消息
|
// 使用消息处理器解析和处理消息
|
||||||
byte[] responseData = messageProcessor.processMessage(
|
byte[] responseData = messageProcessor.processMessage(
|
||||||
Arrays.copyOf(data, length), clientAddress);
|
Arrays.copyOf(data, length), clientAddress, clientSocket);
|
||||||
|
|
||||||
// 发送应答消息
|
// 发送应答消息
|
||||||
if (responseData != null) {
|
if (responseData != null) {
|
||||||
|
|
|
||||||
|
|
@ -211,7 +211,8 @@ public class Jtt808DataController {
|
||||||
return AjaxResult.error(result.get("message").toString()).put("data", result);
|
return AjaxResult.error(result.get("message").toString()).put("data", result);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return AjaxResult.error("发送指令失败: " + e.getMessage());
|
System.err.println("指令下发失败:" + e.getMessage());
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -228,7 +229,8 @@ public class Jtt808DataController {
|
||||||
Map<String, Object> result = jtt808CommandService.sendTextCommandBatch(phoneList, command);
|
Map<String, Object> result = jtt808CommandService.sendTextCommandBatch(phoneList, command);
|
||||||
return AjaxResult.success(result);
|
return AjaxResult.success(result);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return AjaxResult.error("批量发送指令失败: " + e.getMessage());
|
System.err.println("批量发送指令失败: " + e.getMessage());
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -246,7 +248,8 @@ public class Jtt808DataController {
|
||||||
result.put("status", online ? "在线" : "离线");
|
result.put("status", online ? "在线" : "离线");
|
||||||
return AjaxResult.success(result);
|
return AjaxResult.success(result);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return AjaxResult.error("查询在线状态失败: " + e.getMessage());
|
System.err.println("查询在线状态失败: " + e.getMessage());
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -271,7 +274,8 @@ public class Jtt808DataController {
|
||||||
|
|
||||||
return AjaxResult.success(results);
|
return AjaxResult.success(results);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return AjaxResult.error("批量查询在线状态失败: " + e.getMessage());
|
System.err.println("批量查询在线状态失败: " + e.getMessage());
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2,11 +2,9 @@ package com.bonus.sgzb.material.service.impl;
|
||||||
|
|
||||||
import com.bonus.sgzb.material.service.IJtt808CommandService;
|
import com.bonus.sgzb.material.service.IJtt808CommandService;
|
||||||
import com.bonus.sgzb.material.service.IJtt808DataService;
|
import com.bonus.sgzb.material.service.IJtt808DataService;
|
||||||
import com.bonus.sgzb.material.utils.ChannelManager;
|
import com.bonus.sgzb.material.utils.SocketConnectionManager;
|
||||||
import com.bonus.sgzb.material.utils.JTT808MessageBuilder;
|
import com.bonus.sgzb.material.utils.JTT808MessageBuilder;
|
||||||
import com.bonus.sgzb.material.domain.Jtt808Terminal;
|
import com.bonus.sgzb.material.domain.Jtt808Terminal;
|
||||||
import io.netty.channel.Channel;
|
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
@ -26,7 +24,7 @@ public class Jtt808CommandServiceImpl implements IJtt808CommandService {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(Jtt808CommandServiceImpl.class);
|
private static final Logger logger = LoggerFactory.getLogger(Jtt808CommandServiceImpl.class);
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ChannelManager channelManager;
|
private SocketConnectionManager socketConnectionManager;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private IJtt808DataService jtt808DataService;
|
private IJtt808DataService jtt808DataService;
|
||||||
|
|
@ -47,9 +45,8 @@ public class Jtt808CommandServiceImpl implements IJtt808CommandService {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查终端是否在线
|
// 检查终端是否在线(使用 SocketConnectionManager)
|
||||||
Channel channel = channelManager.get(phoneNumber);
|
if (!socketConnectionManager.isOnline(phoneNumber)) {
|
||||||
if (channel == null || !channel.isActive()) {
|
|
||||||
result.put("success", false);
|
result.put("success", false);
|
||||||
result.put("message", "终端离线: " + phoneNumber);
|
result.put("message", "终端离线: " + phoneNumber);
|
||||||
result.put("terminalInfo", terminal);
|
result.put("terminalInfo", terminal);
|
||||||
|
|
@ -68,10 +65,10 @@ public class Jtt808CommandServiceImpl implements IJtt808CommandService {
|
||||||
logger.info("向终端 {} 发送文本指令: {}", phoneNumber, command);
|
logger.info("向终端 {} 发送文本指令: {}", phoneNumber, command);
|
||||||
logger.debug("消息内容: {}", bytesToHex(message));
|
logger.debug("消息内容: {}", bytesToHex(message));
|
||||||
|
|
||||||
ChannelFuture future = channel.writeAndFlush(message);
|
// 使用 SocketConnectionManager 发送数据
|
||||||
future.await(5000); // 等待5秒
|
boolean sent = socketConnectionManager.sendData(phoneNumber, message);
|
||||||
|
|
||||||
if (future.isSuccess()) {
|
if (sent) {
|
||||||
result.put("success", true);
|
result.put("success", true);
|
||||||
result.put("message", "指令发送成功");
|
result.put("message", "指令发送成功");
|
||||||
result.put("phoneNumber", phoneNumber);
|
result.put("phoneNumber", phoneNumber);
|
||||||
|
|
@ -80,8 +77,8 @@ public class Jtt808CommandServiceImpl implements IJtt808CommandService {
|
||||||
logger.info("✅ 指令发送成功: {} -> {}", phoneNumber, command);
|
logger.info("✅ 指令发送成功: {} -> {}", phoneNumber, command);
|
||||||
} else {
|
} else {
|
||||||
result.put("success", false);
|
result.put("success", false);
|
||||||
result.put("message", "指令发送失败: " + future.cause());
|
result.put("message", "指令发送失败");
|
||||||
logger.error("❌ 指令发送失败: {} -> {}, 原因: {}", phoneNumber, command, future.cause());
|
logger.error("❌ 指令发送失败: {} -> {}", phoneNumber, command);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
@ -145,13 +142,19 @@ public class Jtt808CommandServiceImpl implements IJtt808CommandService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isTerminalOnline(String phoneNumber) {
|
public boolean isTerminalOnline(String phoneNumber) {
|
||||||
Channel channel = channelManager.get(phoneNumber);
|
// 从数据库查询终端在线状态
|
||||||
return channel != null && channel.isActive();
|
Jtt808Terminal terminal = jtt808DataService.getTerminalByPhone(phoneNumber);
|
||||||
|
if (terminal == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// onlineStatus: 1=在线, 0=离线
|
||||||
|
return terminal.getOnlineStatus() != null && terminal.getOnlineStatus() == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getTerminalChannel(String phoneNumber) {
|
public Object getTerminalChannel(String phoneNumber) {
|
||||||
return channelManager.get(phoneNumber);
|
// 返回 Socket 连接
|
||||||
|
return socketConnectionManager.getConnection(phoneNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,12 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||||
/**
|
/**
|
||||||
* @author 马三炮
|
* @author 马三炮
|
||||||
* @date 2025/6/5
|
* @date 2025/6/5
|
||||||
|
*
|
||||||
|
* @deprecated 此类已废弃,不再使用。
|
||||||
|
* 原因:项目使用原生 Socket 服务器,而非 Netty 框架。
|
||||||
|
* 请使用 {@link SocketConnectionManager} 代替。
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
public class ChannelManager {
|
public class ChannelManager {
|
||||||
|
|
@ -50,11 +55,23 @@ public class ChannelManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean remove(String terminalPhone) {
|
public boolean remove(String terminalPhone) {
|
||||||
return channelGroup.remove(channelIdMap.remove(terminalPhone));
|
ChannelId channelId = channelIdMap.remove(terminalPhone);
|
||||||
|
if (channelId == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Channel channel = channelGroup.find(channelId);
|
||||||
|
if (channel == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return channelGroup.remove(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Channel get(String terminalPhone) {
|
public Channel get(String terminalPhone) {
|
||||||
return channelGroup.find(channelIdMap.get(terminalPhone));
|
ChannelId channelId = channelIdMap.get(terminalPhone);
|
||||||
|
if (channelId == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return channelGroup.find(channelId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChannelGroup getChannelGroup() {
|
public ChannelGroup getChannelGroup() {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.Socket;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
@ -30,6 +31,9 @@ public class JTT808MessageProcessor {
|
||||||
@Autowired
|
@Autowired
|
||||||
private IGeofenceService geofenceService;
|
private IGeofenceService geofenceService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SocketConnectionManager socketConnectionManager;
|
||||||
|
|
||||||
// JTT808消息ID常量
|
// JTT808消息ID常量
|
||||||
private static final int MSG_TERMINAL_REGISTER = 0x0100; // 终端注册
|
private static final int MSG_TERMINAL_REGISTER = 0x0100; // 终端注册
|
||||||
private static final int MSG_TERMINAL_AUTH = 0x0102; // 终端鉴权
|
private static final int MSG_TERMINAL_AUTH = 0x0102; // 终端鉴权
|
||||||
|
|
@ -50,8 +54,13 @@ public class JTT808MessageProcessor {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理接收到的JTT808消息
|
* 处理接收到的JTT808消息
|
||||||
|
*
|
||||||
|
* @param data 消息数据
|
||||||
|
* @param clientAddress 客户端地址
|
||||||
|
* @param clientSocket 客户端 Socket 连接
|
||||||
|
* @return 应答消息
|
||||||
*/
|
*/
|
||||||
public byte[] processMessage(byte[] data, String clientAddress) {
|
public byte[] processMessage(byte[] data, String clientAddress, Socket clientSocket) {
|
||||||
try {
|
try {
|
||||||
logger.info("开始处理来自客户端 {} 的JTT808消息,数据长度: {}", clientAddress, data.length);
|
logger.info("开始处理来自客户端 {} 的JTT808消息,数据长度: {}", clientAddress, data.length);
|
||||||
|
|
||||||
|
|
@ -90,7 +99,7 @@ public class JTT808MessageProcessor {
|
||||||
clientAddress, String.format("%04X", message.messageId), getMessageTypeName(message.messageId), message.phoneNumber, message.serialNumber);
|
clientAddress, String.format("%04X", message.messageId), getMessageTypeName(message.messageId), message.phoneNumber, message.serialNumber);
|
||||||
|
|
||||||
// 根据消息类型处理
|
// 根据消息类型处理
|
||||||
return handleMessage(message, clientAddress);
|
return handleMessage(message, clientAddress, clientSocket);
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("处理JTT808消息异常,客户端: {}, 错误: {}", clientAddress, e.getMessage(), e);
|
logger.error("处理JTT808消息异常,客户端: {}, 错误: {}", clientAddress, e.getMessage(), e);
|
||||||
|
|
@ -117,8 +126,8 @@ public class JTT808MessageProcessor {
|
||||||
logger.info("========== 测试处理日志中的70字节数据 ==========");
|
logger.info("========== 测试处理日志中的70字节数据 ==========");
|
||||||
logger.info("原始数据: {}", hexData);
|
logger.info("原始数据: {}", hexData);
|
||||||
|
|
||||||
// 调用处理方法
|
// 调用处理方法(测试时传入 null Socket)
|
||||||
byte[] response = processMessage(testData, "TEST_CLIENT");
|
byte[] response = processMessage(testData, "TEST_CLIENT", null);
|
||||||
|
|
||||||
if (response != null) {
|
if (response != null) {
|
||||||
logger.info("测试成功:生成了应答消息,长度: {} bytes", response.length);
|
logger.info("测试成功:生成了应答消息,长度: {} bytes", response.length);
|
||||||
|
|
@ -424,7 +433,7 @@ public class JTT808MessageProcessor {
|
||||||
/**
|
/**
|
||||||
* 根据消息类型处理消息
|
* 根据消息类型处理消息
|
||||||
*/
|
*/
|
||||||
private byte[] handleMessage(JTT808Message message, String clientAddress) {
|
private byte[] handleMessage(JTT808Message message, String clientAddress, Socket clientSocket) {
|
||||||
String messageType = getMessageTypeName(message.messageId);
|
String messageType = getMessageTypeName(message.messageId);
|
||||||
byte[] response = null;
|
byte[] response = null;
|
||||||
int processResult = 0; // 0-成功, 1-失败
|
int processResult = 0; // 0-成功, 1-失败
|
||||||
|
|
@ -436,7 +445,7 @@ public class JTT808MessageProcessor {
|
||||||
response = handleTerminalRegister(message, clientAddress);
|
response = handleTerminalRegister(message, clientAddress);
|
||||||
break;
|
break;
|
||||||
case MSG_TERMINAL_AUTH:
|
case MSG_TERMINAL_AUTH:
|
||||||
response = handleTerminalAuth(message, clientAddress);
|
response = handleTerminalAuth(message, clientAddress, clientSocket);
|
||||||
break;
|
break;
|
||||||
case MSG_HEARTBEAT:
|
case MSG_HEARTBEAT:
|
||||||
response = handleHeartbeat(message, clientAddress);
|
response = handleHeartbeat(message, clientAddress);
|
||||||
|
|
@ -582,18 +591,37 @@ public class JTT808MessageProcessor {
|
||||||
/**
|
/**
|
||||||
* 处理终端鉴权
|
* 处理终端鉴权
|
||||||
*/
|
*/
|
||||||
private byte[] handleTerminalAuth(JTT808Message message, String clientAddress) {
|
private byte[] handleTerminalAuth(JTT808Message message, String clientAddress, Socket clientSocket) {
|
||||||
logger.info("处理终端鉴权: 手机号={}", message.phoneNumber);
|
logger.info("处理终端鉴权: 手机号={}", message.phoneNumber);
|
||||||
|
|
||||||
if (message.body != null) {
|
if (message.body != null) {
|
||||||
String authCode = new String(message.body).trim();
|
String authCode = new String(message.body).trim();
|
||||||
logger.info("鉴权码: {}", authCode);
|
logger.info("鉴权码: {}", authCode);
|
||||||
|
|
||||||
// TODO: 验证鉴权码
|
// 验证鉴权码
|
||||||
boolean authValid = validateAuthCode(message.phoneNumber, authCode);
|
boolean authValid = validateAuthCode(message.phoneNumber, authCode);
|
||||||
|
|
||||||
if (authValid) {
|
if (authValid) {
|
||||||
logger.info("终端 {} 鉴权成功", message.phoneNumber);
|
logger.info("终端 {} 鉴权成功", message.phoneNumber);
|
||||||
|
|
||||||
|
// ✅ 鉴权成功后,将 Socket 连接注册到连接管理器
|
||||||
|
if (clientSocket != null) {
|
||||||
|
boolean registered = socketConnectionManager.addConnection(message.phoneNumber, clientSocket);
|
||||||
|
if (registered) {
|
||||||
|
logger.info("✅ 终端 {} 连接已注册到连接管理器", message.phoneNumber);
|
||||||
|
|
||||||
|
// 更新数据库中的在线状态
|
||||||
|
try {
|
||||||
|
jtt808DataService.updateTerminalOnlineStatus(message.phoneNumber, true, clientAddress);
|
||||||
|
logger.info("✅ 终端 {} 在线状态已更新到数据库", message.phoneNumber);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("更新终端在线状态异常: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.warn("⚠️ 终端 {} 连接注册失败", message.phoneNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return buildGeneralResponse(message, RESULT_SUCCESS);
|
return buildGeneralResponse(message, RESULT_SUCCESS);
|
||||||
} else {
|
} else {
|
||||||
logger.warn("终端 {} 鉴权失败", message.phoneNumber);
|
logger.warn("终端 {} 鉴权失败", message.phoneNumber);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue