基于Tomcat 8与Java 7的WebSocket聊天室源码解析与部署实践
简介这是一套基于Tomcat8、Java7与ExtJS构建的WebSocket实时聊天室完整源码面向具备Java Web基础、希望掌握全双工通信开发的进阶学习者。项目以JSR 356标准API为核心服务端通过ServerEndpoint端点处理连接、消息与关闭等生命周期事件客户端借助ExtJS组件搭建输入框、按钮与聊天窗口并实现用户登录、消息广播与历史记录展示等核心逻辑同时涉及SSL/TLS加密、连接池管理与消息缓存等安全与性能优化思路。资源包共1753个文件以1359个gif、158个scss、114个png等前端界面与样式素材为主辅以50个js脚本、34个css样式、11个jar依赖及少量java、class与xml配置压缩后约7.01MB目录结构完整。目前已有262人学习下载适合作为理解WebSocket通信协议、服务端编程与ExtJS客户端集成的实践案例也可用于二次开发与课程设计参考。1. 从一次“古董级”技术栈选型说起这套聊天室源码到底能跑通什么前阵子帮一个做企业内训的朋友看一套老系统的维护问题对方丢过来一个压缩包说“这套聊天室源码在测试环境跑不起来你帮我看看”。打开一看技术栈是 Tomcat 8 Java 7 ExtJS通信层用的是原生 WebSocket。说实话第一反应是这组合有点年头了但仔细翻完代码之后发现它反而是一份很适合拿来理解 WebSocket 全链路落地的最小可用样本——没有 Spring Boot 的自动装配帮你兜底没有 Netty 帮你封装协议细节从握手、会话管理到消息广播全得自己写。这套源码解决的核心问题很明确在一个纯 Java Web 容器里不依赖任何第三方实时通信框架实现一个多人在线聊天室。前端用 ExtJS 做界面和消息渲染后端用javax.websocket标准 API 处理连接生命周期Tomcat 8 作为 Servlet 容器兼 WebSocket 服务端。适合谁看一是需要维护存量老项目的后端二是想搞清楚 WebSocket 在 Servlet 容器里到底怎么跑起来的学习者三是手头有类似内网通信需求、不想引入重型中间件的团队。它不适合什么场景高并发、需要集群横向扩展、需要消息持久化和离线推送的生产级 IM。这套代码的定位是“能跑通、能看懂、能改”不是“能扛住”。下面我按实际拆包和部署的顺序把这份源码的结构、跑起来的步骤、参数怎么调、坑在哪一层层说清楚。2. 拆开源码包目录结构与 WebSocket 服务端核心类2.1 工程目录一览与各模块职责拿到源码包之后先别急着往 IDE 里导。我一般习惯先在命令行下把目录树扫一遍确认构建方式和依赖范围。这套工程的典型结构如下chatroom/ ├── src/ │ └── main/ │ ├── java/ │ │ └── com/example/chat/ │ │ ├── config/ │ │ │ └── WebSocketConfig.java │ │ ├── endpoint/ │ │ │ ├── ChatEndpoint.java │ │ │ └── ChatMessage.java │ │ └── util/ │ │ └── SessionManager.java │ ├── resources/ │ │ └── log4j.properties │ └── webapp/ │ ├── WEB-INF/ │ │ └── web.xml │ ├── extjs/ │ │ ├── ext-all.js │ │ └── resources/ │ └── index.html ├── lib/ │ ├── javax.websocket-api.jar │ └── gson-2.8.9.jar └── pom.xml (或 build.xml)几个关键点ChatEndpoint.java是整个服务端的入口用ServerEndpoint注解声明 WebSocket 端点路径SessionManager.java负责维护在线会话集合通常是ConcurrentHashMap或者CopyOnWriteArraySetChatMessage.java是消息体 POJO用来做 JSON 序列化和反序列化。前端index.html里通过 ExtJS 的组件树搭建聊天窗口再用原生WebSocket对象连到服务端。注意如果源码包里没有pom.xml而是build.xml说明是 Ant 构建的老工程导入 IDEA 时需要手动配置 Artifact 或者转成 Maven 结构否则 Tomcat 部署时会找不到编译输出目录。2.2ServerEndpoint注解与生命周期方法WebSocket 服务端的核心就是这个注解类。它不像 Spring 的Controller那样由容器托管 Bean而是由 WebSocket 容器这里是 Tomcat直接实例化和管理。每个客户端连接对应一个 Endpoint 实例生命周期方法用注解标记ServerEndpoint(value /chat/{roomId}, encoders {MessageEncoder.class}, decoders {MessageDecoder.class}) public class ChatEndpoint { // 每个连接独立持有自己的 session private Session session; private String roomId; OnOpen public void onOpen(Session session, PathParam(roomId) String roomId) { this.session session; this.roomId roomId; // 将当前连接加入房间会话池 SessionManager.add(roomId, this); // 广播“某某上线”系统消息 SessionManager.broadcast(roomId, new ChatMessage(system, 用户加入房间)); } OnMessage public void onMessage(String message, Session session) { // 解析 JSON按消息类型分发 ChatMessage msg ChatMessage.fromJson(message); if (text.equals(msg.getType())) { SessionManager.broadcast(roomId, msg); } } OnClose public void onClose(Session session) { SessionManager.remove(roomId, this); SessionManager.broadcast(roomId, new ChatMessage(system, 用户离开房间)); } OnError public void onError(Session session, Throwable error) { // 连接异常时清理会话避免僵尸连接堆积 SessionManager.remove(roomId, this); error.printStackTrace(); } }逻辑说明OnOpen在握手成功后触发此时session对象已经可用可以拿到session.getId()做唯一标识。OnMessage是消息入口这里用字符串接收再手动解析 JSON而不是直接用 Decoder是为了兼容前端 ExtJS 发过来的格式差异。OnClose和OnError必须都做会话清理否则客户端异常断线时SessionManager里会残留无效 session后续广播时抛IllegalStateException。参数说明value /chat/{roomId}中的{roomId}是路径参数前端连接时写成ws://host:port/chat/room1即可区分不同房间。encoders和decoders是可选的如果消息体简单直接收发字符串更省事。2.3SessionManager的并发容器选型会话管理是这套源码里最容易被改出 bug 的地方。常见做法是用ConcurrentHashMapString, SetChatEndpoint按房间分组每个房间的 Set 用ConcurrentHashMap.newKeySet()创建public class SessionManager { // 房间ID - 该房间内的端点集合 private static final ConcurrentHashMapString, SetChatEndpoint ROOMS new ConcurrentHashMap(); public static void add(String roomId, ChatEndpoint endpoint) { ROOMS.computeIfAbsent(roomId, k - ConcurrentHashMap.newKeySet()) .add(endpoint); } public static void remove(String roomId, ChatEndpoint endpoint) { SetChatEndpoint set ROOMS.get(roomId); if (set ! null) { set.remove(endpoint); if (set.isEmpty()) { ROOMS.remove(roomId); } } } public static void broadcast(String roomId, ChatMessage msg) { SetChatEndpoint set ROOMS.get(roomId); if (set null) return; String json msg.toJson(); for (ChatEndpoint endpoint : set) { try { // 同步发送Tomcat 内部会做缓冲 endpoint.session.getBasicRemote().sendText(json); } catch (IOException e) { // 发送失败说明连接已断移除 set.remove(endpoint); } } } }为什么不用CopyOnWriteArraySet因为广播频率高时写操作会频繁复制整个数组内存开销大。ConcurrentHashMap.newKeySet()底层是ConcurrentHashMap的键视图增删改查都是分段锁或 CAS更适合这种读多写少但写操作也不少的场景。另一个坑是broadcast里遍历时直接调set.remove()在ConcurrentHashMap的键视图里是安全的但如果是HashSet就会抛ConcurrentModificationException。3. 前端 ExtJS 与 WebSocket 的对接消息渲染与心跳保活3.1 ExtJS 组件树搭建聊天面板ExtJS 的写法跟现代前端框架差别很大它是基于配置对象的组件化所有 UI 元素通过Ext.create或者Ext.define声明。聊天室的主界面通常是一个Viewport里面放一个panel作为消息展示区一个form放输入框和发送按钮Ext.define(Chat.view.Main, { extend: Ext.container.Viewport, layout: border, items: [{ region: center, xtype: panel, id: msgPanel, title: 聊天室, autoScroll: true, html: div idmsgList/div }, { region: south, height: 80, xtype: form, items: [{ xtype: textfield, id: msgInput, emptyText: 输入消息..., anchor: 100% }, { xtype: button, text: 发送, handler: function() { var input Ext.getCmp(msgInput); var text input.getValue(); if (text ws ws.readyState WebSocket.OPEN) { ws.send(JSON.stringify({ type: text, content: text, sender: userName })); input.setValue(); } } }] }] });逻辑说明autoScroll: true让消息面板在内容超出时自动出现滚动条。消息渲染没有用 ExtJS 的grid或dataview而是直接操作innerHTML往#msgList里追加div这样性能更好也避免 ExtJS 组件频繁创建销毁带来的内存问题。发送前检查ws.readyState WebSocket.OPEN是必须的否则连接断开后点发送会静默失败。参数说明userName是页面加载时让用户输入的昵称存在全局变量里。JSON.stringify把消息体序列化成字符串服务端ChatMessage.fromJson按同样结构解析。3.2 WebSocket 连接建立与心跳机制前端连接服务端的代码通常写在index.html的Ext.onReady里var ws null; var heartbeatTimer null; function connect() { var protocol location.protocol https: ? wss: : ws:; var wsUrl protocol // location.host /chat/room1; ws new WebSocket(wsUrl); ws.onopen function() { console.log(WebSocket connected); // 每 30 秒发一次心跳防止中间层断开空闲连接 heartbeatTimer setInterval(function() { if (ws.readyState WebSocket.OPEN) { ws.send(JSON.stringify({type: ping})); } }, 30000); }; ws.onmessage function(event) { var msg JSON.parse(event.data); if (msg.type ping) return; // 忽略心跳响应 appendMessage(msg); }; ws.onclose function() { console.log(WebSocket closed, retry in 3s); clearInterval(heartbeatTimer); // 断线重连避免用户手动刷新 setTimeout(connect, 3000); }; ws.onerror function(err) { console.error(WebSocket error, err); ws.close(); }; }逻辑说明心跳的作用是防止 Nginx 或 Tomcat 的connectionTimeout把空闲连接掐掉。Tomcat 8 默认的 WebSocket 空闲超时是 300 秒org.apache.tomcat.websocket.BLOCKING_SEND_TIMEOUT和sessionTimeout相关30 秒发一次 ping 足够安全。onclose里做 3 秒延迟重连避免服务端重启时客户端疯狂重试打满连接数。参数说明location.host自动取当前页面的域名和端口部署到不同环境不用改代码。心跳间隔 30000 毫秒可以根据实际网络环境调整内网可以放到 60 秒公网建议不超过 30 秒。3.3 消息编码与 ExtJS 渲染的配合服务端广播出来的 JSON 结构要和前端appendMessage的解析逻辑对齐。典型的消息体{ type: text, sender: 张三, content: 大家好, timestamp: 1700000000000 }前端渲染时按type区分系统消息和用户消息系统消息用灰色斜体用户消息用正常字体加粗显示昵称function appendMessage(msg) { var msgList document.getElementById(msgList); var div document.createElement(div); if (msg.type system) { div.style.color #999; div.style.fontStyle italic; div.innerText msg.content; } else { var time new Date(msg.timestamp).toLocaleTimeString(); div.innerHTML b msg.sender /b time br msg.content; } msgList.appendChild(div); // 滚动到底部 var panel Ext.getCmp(msgPanel); panel.body.dom.scrollTop panel.body.dom.scrollHeight; }这里有个细节ExtJS 的panel.body.dom才是实际滚动的 DOM 元素直接对panel调scrollTop是无效的。另外innerHTML拼接时如果msg.content里包含 HTML 标签会有 XSS 风险生产环境需要做转义。这套源码作为学习样本没做转义处理改的时候记得补上。4. 部署与联调Tomcat 8 Java 7 环境下的参数配置4.1 Tomcat 8 的 WebSocket 支持与 JDK 版本约束Tomcat 8 从 8.0.x 开始完整支持 JSR-356Java WebSocket 1.0但有个硬性前提JDK 必须是 7 及以上。Java 7 自带的javax.websocketAPI 在rt.jar里没有需要额外引入javax.websocket-api.jar或者直接用 Tomcat 的lib目录下自带的tomcat-websocket.jar和websocket-api.jar。我一般建议把这两个 jar 放到工程的WEB-INF/lib下避免不同 Tomcat 版本之间的类加载冲突。部署方式有两种一是把编译好的war包丢进webapps目录二是直接在 IDE 里配置 Tomcat Server 跑 exploded 模式。后者调试方便改 Java 代码热加载快但改web.xml或注解配置时需要重启。注意Java 7 编译时-source 1.7 -target 1.7但 Tomcat 8 运行时的 JVM 可以是 Java 8这个不冲突。如果编译报ServerEndpoint找不到符号检查javax.websocket-api.jar是否在 classpath 里。4.2web.xml与server.xml的关键配置项web.xml里不需要额外声明 WebSocket 的 Servlet因为ServerEndpoint是注解扫描的。但需要确保metadata-completefalse否则容器会跳过注解扫描web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee version3.1 metadata-completefalse display-nameChatRoom/display-name welcome-file-list welcome-fileindex.html/welcome-file /welcome-file-list /web-appserver.xml里跟 WebSocket 相关的参数主要是Connector的connectionTimeout和maxThreads。默认connectionTimeout20000是 20 秒但 WebSocket 连接建立后不受这个限制受的是org.apache.tomcat.websocket包下的sessionTimeout。可以在context.xml里加Context Parameter nameorg.apache.tomcat.websocket.sessionTimeout value300000 overridefalse/ /Context参数说明sessionTimeout单位是毫秒300000 即 5 分钟。如果前端心跳间隔是 30 秒这个值设 5 分钟足够。设太小会导致正常聊天时连接被容器主动断开设太大则僵尸连接回收慢。4.3 联调时用浏览器控制台和wscat验证部署完成后先用浏览器打开index.htmlF12 看 Network 面板的 WS 标签确认握手返回 101 Switching Protocols。如果返回 404检查ServerEndpoint的路径和web.xml的url-pattern是否冲突如果返回 500看 Tomcat 日志里有没有ClassNotFoundException或NoClassDefFoundError。命令行下可以用wscat快速验证服务端是否正常# 安装 wscat需要 Node.js 环境 npm install -g wscat # 连接服务端 wscat -c ws://localhost:8080/chat/room1 # 连接成功后输入消息观察是否广播回来 {type:text,sender:tester,content:hello}如果wscat能连上但浏览器连不上多半是跨域或者路径问题。WebSocket 没有同源策略限制但 Tomcat 的CORS过滤器如果配置了Access-Control-Allow-Origin可能会拦截握手请求。检查web.xml里有没有不必要的 Filter 映射到/*。5. 避坑排查这套源码跑不起来时先看这五条5.1 现象握手返回 404浏览器控制台报WebSocket connection failed原因ServerEndpoint的 value 路径和实际访问路径不一致。比如注解写的是/chat/{roomId}但前端连的是/chatroom或者 Tomcat 的 context path 不是根路径。解决在浏览器里直接访问http://localhost:8080/chat/room1如果返回 404 说明路径没匹配上。检查server.xml里Context path的配置以及ServerEndpoint是否被 Spring 的Component扫描覆盖导致重复注册。5.2 现象连接建立后立刻断开OnClose被频繁触发原因Tomcat 的sessionTimeout设得太短或者前端没有发心跳中间有 Nginx 之类的反向代理把空闲连接掐了。解决先确认context.xml里的sessionTimeout值再检查前端心跳定时器是否正常启动。如果用了 Nginx需要在nginx.conf里加proxy_read_timeout 300s;和proxy_send_timeout 300s;并且proxy_set_header Upgrade $http_upgrade;和proxy_set_header Connection upgrade;这两行不能少。5.3 现象广播消息时抛IllegalStateException: The remote endpoint was in state [TEXT_FULL_WRITING]原因多个线程同时对同一个session调getBasicRemote().sendText()Tomcat 的BasicRemote不是线程安全的。聊天室里多个用户同时发消息时广播线程池可能并发写同一个连接。解决把getBasicRemote()换成getAsyncRemote()或者对每个 session 的发送操作加同步锁。更稳妥的做法是每个 session 维护一个发送队列单线程消费。这套源码里如果没做处理高并发下必现这个异常。5.4 现象ExtJS 页面加载后一片空白控制台报Ext is not defined原因ext-all.js的路径不对或者 ExtJS 版本和源码里用的 API 不匹配。ExtJS 4 和 6 的组件配置差异很大比如autoScroll在 6 里改成了scrollable。解决确认index.html里script srcextjs/ext-all.js的相对路径正确。如果用的是 ExtJS 6把autoScroll: true改成scrollable: trueExt.getCmp在 6 里仍然可用但推荐用Ext.ComponentQuery.query。5.5 现象Java 7 编译报diamond operator is not supported原因源码里用了 Java 8 的菱形语法new ConcurrentHashMap()但pom.xml或 IDE 的编译级别设的是 1.7。解决要么把编译级别升到 1.8Tomcat 8 支持 Java 8要么把菱形语法改成显式类型声明new ConcurrentHashMapString, SetChatEndpoint()。Java 7 支持菱形语法但仅限于匿名内部类以外的场景如果报错说明 IDE 的 Language Level 没设对。6. 进阶改造把单机聊天室改成可水平扩展的结构这套源码跑通之后最现实的问题是单台 Tomcat 的 WebSocket 连接数有上限而且服务重启时所有连接全断。如果想让多个用户连到不同实例上还能互相聊天就得引入外部消息中转。常见做法是接 Redis 的 Pub/Sub每个 Tomcat 实例订阅同一个频道收到消息后广播给本机持有的 session。改造的核心是替换SessionManager.broadcast的实现public class RedisSessionManager { private static final JedisPool POOL new JedisPool(127.0.0.1, 6379); private static final String CHANNEL chatroom; // 本机持有的会话只负责推送给本地连接 private static final ConcurrentHashMapString, SetChatEndpoint LOCAL new ConcurrentHashMap(); public static void broadcast(String roomId, ChatMessage msg) { // 不直接推本地而是发到 Redis由订阅线程统一处理 try (Jedis jedis POOL.getResource()) { jedis.publish(CHANNEL, roomId | msg.toJson()); } } // 每个实例启动时开一个订阅线程 public static void startSubscriber() { new Thread(() - { try (Jedis jedis POOL.getResource()) { jedis.subscribe(new JedisPubSub() { Override public void onMessage(String channel, String message) { String[] parts message.split(\\|, 2); String roomId parts[0]; String json parts[1]; // 只推给本机持有的 session SetChatEndpoint set LOCAL.get(roomId); if (set ! null) { for (ChatEndpoint ep : set) { try { ep.session.getBasicRemote().sendText(json); } catch (IOException ignored) {} } } } }, CHANNEL); } }).start(); } }逻辑说明broadcast不再直接遍历本地 session而是把消息 publish 到 Redis 频道。每个实例的订阅线程收到消息后只推给本机LOCAL里持有的连接。这样无论用户连到哪台 Tomcat消息都能通过 Redis 中转到达所有实例。参数说明CHANNEL名字可以按业务改roomId | json的分隔符要选消息体里不会出现的字符竖线一般安全。验证方法起两个 Tomcat 实例端口分别设 8080 和 8081前端页面分别连这两个端口在 8080 发消息8081 的页面应该能收到。如果收不到先检查 Redis 的subscribe是否阻塞在主线程导致 Tomcat 启动卡住——订阅线程必须单独起不能放在PostConstruct里同步执行。从那以后我每次改 WebSocket 广播逻辑都强制先在两个实例上跑一遍跨机通信确认 Redis 订阅线程正常消费再上生产。这套源码作为起点改到能水平扩展大概需要动三个类SessionManager、ChatEndpoint的OnOpen注册逻辑、以及前端连接地址的配置化。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

双色散信道为何难处理?从信道建模到BEM与压缩感知的工程实践

双色散信道为何难处理?从信道建模到BEM与压缩感知的工程实践

通信系统设计里,多数工程师最先学会处理两类信道:频率选择性信道靠OFDM加上均衡来抗,时间选择性信道靠交织、导频估计再插值来抗。但如果遇到 double-dispersive channel,也就是双色散信道,这两套知识往往会同时失灵。…

2026/10/14 21:51:58 阅读更多 →
Hadoop单机版部署测试全流程:从环境准备到WordCount踩坑记录

Hadoop单机版部署测试全流程:从环境准备到WordCount踩坑记录

折腾了整整一下午,终于把 Hadoop 单机版部署测试完整跑通了。这事情说起来不复杂,但坑确实不少:环境变量、SSH 免密、格式化时机、进程死活、作业卡住……每一项都能让你怀疑人生。如果你现在也在准备从零部署一套 Hadoop,我的建议…

2026/10/14 21:51:58 阅读更多 →
概率DP与容错机制:状态设计、阈值截断与随机过程应用

概率DP与容错机制:状态设计、阈值截断与随机过程应用

刷题刷到P10504的时候,我第一反应是:一个“普及”的题,怎么标签里挂上了“期望”和“动态规划”?等沉下心把它完整啃下来才发现,这题之所以定为普及,并不是因为期望DP的概念有多深,而是题目把“…

2026/10/14 21:51:58 阅读更多 →

最新新闻

Java实现可交互贝塞尔曲线:原理、Swing绘制与优化

Java实现可交互贝塞尔曲线:原理、Swing绘制与优化

作为一个常年跟图形界面打交道的Java开发者,我早就想写一篇关于贝塞尔曲线的实操总结了。起因很简单:有一次做Canvas绘图工具,需要用鼠标拖拽顶点实时调整曲线形状,而手写三阶曲线公式加交互逻辑时,参数一多就乱了。后…

2026/10/14 22:38:06 阅读更多 →
社区超市促销方案PPT:用Copilot+PPT快速生成可执行方案

社区超市促销方案PPT:用Copilot+PPT快速生成可执行方案

1. 社区超市做促销,为什么总卡在"方案"这一步社区超市做促销这件事,说大不大,说小也不小。一家三百到八百平米的社区店,一个月做两三次促销是常态,节假日、店庆、换季清仓、生鲜引流、会员日,理由…

2026/10/14 22:38:06 阅读更多 →
电商付费流量怎么投?先算清ROI盈亏平衡

电商付费流量怎么投?先算清ROI盈亏平衡

做店铺运营这些年,后台收到最多的私信基本绕不开同一个问题:我到底要不要投付费流量?有人靠它翻盘,有人一天烧掉几千块只换来几十个点击,同样是花钱,结果却天差地别。问题从来不是“该不该投”,…

2026/10/14 22:38:06 阅读更多 →
文档识别转换工作台功能需求文档怎么写才能可落地?

文档识别转换工作台功能需求文档怎么写才能可落地?

文档识别转换工作台,这四个词拆开看每个都认识,合在一起却经常被做成一堆四不像的demo。我在过去几年里接触过不少团队做这类系统,最典型的情况是:需求文档里写了一句话"支持OCR识别和各种格式转换",然后开发…

2026/10/14 22:38:06 阅读更多 →
网络工程师必修课:静态路由与路由表工作原理深度解析

网络工程师必修课:静态路由与路由表工作原理深度解析

1. 这不是“打卡式学习”,而是网络工程师能力筑基的关键第三天HCIA_R&S——这个缩写在某高校网络实验室的白板上被反复圈出,旁边贴着一张手写的日程表:“Day01:OSI七层模型具象化拆解;Day02:IP地址与子…

2026/10/14 22:38:06 阅读更多 →
SuperMap GIS基础产品FAQ集锦:高频问题排查与原理

SuperMap GIS基础产品FAQ集锦:高频问题排查与原理

用“iDesktop打开UDB数据源总是提示被占用”“切片缓存明明生成了,iServer却读不到”——这些几乎每天都会被问到的“老问题”就是这份《SuperMap GIS基础产品FAQ集锦(20260126)》的起点。它既不是官方文档的简单搬运,也不是把几个常见报错列出来就算完事…

2026/10/14 22:37:04 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →