3 changed files with 279 additions and 0 deletions
-
BINdevice/sub_spray-mock-server-1.0.1.jar
-
158src/main/java/com/qyft/ms/device/server/TcpServer.java
-
121src/main/java/com/qyft/ms/device/service/TaskQueueManager.java
@ -0,0 +1,158 @@ |
|||||
|
package com.qyft.ms.device.server; |
||||
|
|
||||
|
import com.qyft.ms.device.config.TcpConfig; |
||||
|
import io.netty.bootstrap.ServerBootstrap; |
||||
|
import io.netty.buffer.Unpooled; |
||||
|
import io.netty.channel.*; |
||||
|
import io.netty.channel.group.ChannelGroup; |
||||
|
import io.netty.channel.group.DefaultChannelGroup; |
||||
|
import io.netty.channel.nio.NioEventLoopGroup; |
||||
|
import io.netty.channel.socket.SocketChannel; |
||||
|
import io.netty.channel.socket.nio.NioServerSocketChannel; |
||||
|
import io.netty.handler.codec.json.JsonObjectDecoder; |
||||
|
import io.netty.util.CharsetUtil; |
||||
|
import io.netty.util.concurrent.GlobalEventExecutor; |
||||
|
import jakarta.annotation.PostConstruct; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
@Slf4j |
||||
|
@Component |
||||
|
@RequiredArgsConstructor |
||||
|
public class TcpServer { |
||||
|
|
||||
|
private final TcpConfig tcpConfig; |
||||
|
|
||||
|
private EventLoopGroup bossGroup; |
||||
|
private EventLoopGroup workerGroup; |
||||
|
|
||||
|
private final ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); |
||||
|
|
||||
|
/** |
||||
|
* 初始化方法,在Spring容器启动后调用 |
||||
|
*/ |
||||
|
@PostConstruct |
||||
|
public void init() { |
||||
|
if (tcpConfig.isEnable()) { |
||||
|
new Thread(this::start).start(); // 在独立线程中启动TCP服务器 |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 启动TCP服务器的方法 |
||||
|
*/ |
||||
|
public void start() { |
||||
|
bossGroup = new NioEventLoopGroup(1); // 创建Boss线程组 |
||||
|
workerGroup = new NioEventLoopGroup(); // 创建Worker线程组 |
||||
|
try { |
||||
|
ServerBootstrap bootstrap = new ServerBootstrap(); |
||||
|
bootstrap.group(bossGroup, workerGroup) |
||||
|
.channel(NioServerSocketChannel.class) |
||||
|
.childHandler(new ChannelInitializer<SocketChannel>() { |
||||
|
@Override |
||||
|
protected void initChannel(SocketChannel ch) { |
||||
|
// 添加JSON对象解码器到ChannelPipeline |
||||
|
ch.pipeline().addLast(new JsonObjectDecoder()); |
||||
|
// 添加TCP消息处理器到ChannelPipeline |
||||
|
ch.pipeline().addLast(new TcpMessageHandler()); |
||||
|
// 添加客户端到ChannelGroup |
||||
|
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() { |
||||
|
@Override |
||||
|
public void channelActive(ChannelHandlerContext ctx) throws Exception { |
||||
|
clients.add(ctx.channel()); // 将新连接的客户端添加到ChannelGroup |
||||
|
super.channelActive(ctx); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void channelInactive(ChannelHandlerContext ctx) throws Exception { |
||||
|
clients.remove(ctx.channel()); // 从ChannelGroup中移除断开连接的客户端 |
||||
|
super.channelInactive(ctx); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}) |
||||
|
.option(ChannelOption.SO_BACKLOG, 128) |
||||
|
.childOption(ChannelOption.SO_KEEPALIVE, true); |
||||
|
|
||||
|
// 绑定并开始接受传入连接 |
||||
|
ChannelFuture future = bootstrap.bind(tcpConfig.getPort()).sync(); |
||||
|
log.info("TCP服务器已启动,监听端口: {}", tcpConfig.getPort()); |
||||
|
|
||||
|
// 等待服务器套接字关闭 |
||||
|
future.channel().closeFuture().sync(); |
||||
|
} catch (InterruptedException e) { |
||||
|
log.error("TCP服务器启动过程中发生中断: {}", e.getMessage(), e); |
||||
|
} finally { |
||||
|
// 优雅地关闭EventLoopGroup |
||||
|
workerGroup.shutdownGracefully(); |
||||
|
bossGroup.shutdownGracefully(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 服务器端推送消息的方法 |
||||
|
* @param message 要推送的消息内容 |
||||
|
*/ |
||||
|
public void sendMessage(String message) { |
||||
|
clients.writeAndFlush(Unpooled.copiedBuffer(message, CharsetUtil.UTF_8)); // 将消息推送给所有客户端 |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 停止TCP服务器的方法 |
||||
|
*/ |
||||
|
public void stop() { |
||||
|
if (workerGroup != null) { |
||||
|
workerGroup.shutdownGracefully(); // 优雅地关闭Worker线程组 |
||||
|
} |
||||
|
if (bossGroup != null) { |
||||
|
bossGroup.shutdownGracefully(); // 优雅地关闭Boss线程组 |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 内部类,处理TCP消息事件 |
||||
|
private static class TcpMessageHandler extends ChannelInboundHandlerAdapter { |
||||
|
|
||||
|
/** |
||||
|
* 处理接收到的消息 |
||||
|
* @param ctx 通道处理上下文 |
||||
|
* @param msg 接收到的消息 |
||||
|
*/ |
||||
|
@Override |
||||
|
public void channelRead(ChannelHandlerContext ctx, Object msg) { |
||||
|
// 记录接收到的消息 |
||||
|
log.info("接收到消息: {}", msg.toString()); |
||||
|
|
||||
|
// 将ByteBuf转换为字符串 |
||||
|
String messageStr = ((io.netty.buffer.ByteBuf) msg).toString(CharsetUtil.UTF_8); |
||||
|
|
||||
|
// 解析JSON对象 |
||||
|
cn.hutool.json.JSONObject jsonObject = cn.hutool.json.JSONUtil.parseObj(messageStr); |
||||
|
|
||||
|
// 可以在这里添加消息处理逻辑 |
||||
|
// 例如:处理特定字段 |
||||
|
String someField = jsonObject.getStr("someField"); |
||||
|
log.info("解析后的字段值: {}", someField); |
||||
|
|
||||
|
// 新增:五秒后推送消息 |
||||
|
Runnable task = () -> { |
||||
|
String responseMessage = "服务器推送消息: " + someField; |
||||
|
ctx.writeAndFlush(Unpooled.copiedBuffer(responseMessage, CharsetUtil.UTF_8)); // 推送消息给客户端 |
||||
|
log.info("五秒后推送消息: {}", responseMessage); |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 处理异常事件 |
||||
|
* @param ctx 通道处理上下文 |
||||
|
* @param cause 异常对象 |
||||
|
*/ |
||||
|
@Override |
||||
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { |
||||
|
// 记录异常日志 |
||||
|
log.error("TCP连接发生异常: {}", cause.getMessage(), cause); |
||||
|
// 关闭当前channel |
||||
|
ctx.close(); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,121 @@ |
|||||
|
package com.qyft.ms.device.service; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
import java.util.UUID; |
||||
|
import java.util.concurrent.*; |
||||
|
import java.util.concurrent.atomic.AtomicBoolean; |
||||
|
|
||||
|
@Slf4j |
||||
|
@Component |
||||
|
public class TaskQueueManager { |
||||
|
|
||||
|
// 任务队列,用于存储待执行的任务 |
||||
|
private final CopyOnWriteArrayList<TaskWrapper> taskQueue; |
||||
|
// 执行任务的线程池 |
||||
|
private final ExecutorService executorService; |
||||
|
// 标记任务队列是否暂停 |
||||
|
private final AtomicBoolean isPaused; |
||||
|
|
||||
|
/** |
||||
|
* @param taskId 唯一标识 |
||||
|
*/ // 新增任务包装类 |
||||
|
private record TaskWrapper(String taskId, Runnable task) { |
||||
|
|
||||
|
} |
||||
|
/** |
||||
|
* 构造函数,初始化任务队列管理器 |
||||
|
*/ |
||||
|
public TaskQueueManager() { |
||||
|
this.taskQueue = new CopyOnWriteArrayList<>(); |
||||
|
this.executorService = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()); |
||||
|
this.isPaused = new AtomicBoolean(false); |
||||
|
} |
||||
|
|
||||
|
public void addTask(Runnable task) { |
||||
|
addTask(UUID.randomUUID().toString(), task); // 自动生成 taskId |
||||
|
} |
||||
|
|
||||
|
private void addTask(String taskId, Runnable task) { |
||||
|
taskQueue.add(new TaskWrapper(taskId, task)); |
||||
|
log.info("添加到任务队列: taskId={}, task={}", taskId, task); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 在指定位置添加任务到任务队列 |
||||
|
* @param index 插入位置 |
||||
|
* @param task 待添加的任务 |
||||
|
*/ |
||||
|
public void addTask(int index, Runnable task) { |
||||
|
addTask(index, UUID.randomUUID().toString(), task); // 自动生成 taskId |
||||
|
} |
||||
|
|
||||
|
private void addTask(int index, String taskId, Runnable task) { |
||||
|
taskQueue.add(index, new TaskWrapper(taskId, task)); |
||||
|
log.info("在位置 {} 添加到任务队列: taskId={}, task={}", index, taskId, task); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取当前任务队列中的任务列表 |
||||
|
* @return 任务列表 |
||||
|
*/ |
||||
|
public List<Runnable> getTasks() { |
||||
|
List<Runnable> tasks = new ArrayList<>(); |
||||
|
for (TaskWrapper wrapper : taskQueue) { |
||||
|
tasks.add(wrapper.task()); |
||||
|
} |
||||
|
return tasks; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 启动任务队列管理器,开始处理任务 |
||||
|
*/ |
||||
|
public void start() { |
||||
|
executorService.submit(() -> { // 提交一个任务到线程池 |
||||
|
while (!executorService.isShutdown()) { // 循环直到线程池关闭 |
||||
|
try { |
||||
|
while (isPaused.get()) { // 如果任务队列暂停,则休眠 |
||||
|
Thread.sleep(100); |
||||
|
} |
||||
|
TaskWrapper taskWrapper = taskQueue.remove(0); // 从队列中取出一个任务,如果队列为空则阻塞等待 |
||||
|
taskWrapper.task().run(); // 执行任务 |
||||
|
} catch (InterruptedException e) { |
||||
|
Thread.currentThread().interrupt(); // 恢复中断状态 |
||||
|
break; // 如果线程被中断,则退出循环 |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 暂停任务队列 |
||||
|
*/ |
||||
|
public void pause() { |
||||
|
isPaused.set(true); // 设置暂停标志为true |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 恢复任务队列 |
||||
|
*/ |
||||
|
public void resume() { |
||||
|
isPaused.set(false); // 设置暂停标志为false |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 关闭任务队列管理器 |
||||
|
*/ |
||||
|
public void shutdown() { |
||||
|
executorService.shutdown(); // 关闭线程池 |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 检查任务队列管理器是否已关闭 |
||||
|
* @return 如果已关闭则返回true,否则返回false |
||||
|
*/ |
||||
|
public boolean isShutdown() { |
||||
|
return executorService.isShutdown(); |
||||
|
} |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue