资讯动态

基于SpringBoot+WebSocket的轻量级聊天室实战:从原理到部署

发布时间:2026/8/31 15:10:19 来源:尧图企业网站定制
简介这是一款面向计算机相关专业在校学生、初学者及课程设计者的轻量级在线聊天室实战项目基于SpringBoot与WebSocket构建解决传统JSPXML方案维护性差、技术栈陈旧等问题适用于毕设、课设、作业演示及Web实时通信入门学习。资源包共115个文件含21个核心Java后端逻辑类、7个前端交互JS脚本、4个CSS样式文件、2个HTML页面及Thymeleaf模板辅以yml配置、SQL建表语句与README说明文档整体仅1.59MB结构清晰、去冗余、易二次开发。已有171人下载学习项目源自高分平均96分本科毕业设计所有代码均经实测运行通过配套多张功能演示GIF如登录、消息收发等场景直观呈现交互效果同时提供完整前后端分离式实现思路、注解驱动的DAO层封装、WebSocket会话管理范例及Thymeleaf动态渲染实践便于理解现代SpringBoot Web应用开发全流程。 一个聊天室项目看起来简单但真要动手做从技术选型到部署上线坑其实不少。之前帮朋友搭过一个内部用的轻量级在线聊天室基于SpringBoot和WebSocket代码量不大但把该踩的坑基本都趟了一遍。这篇文章把完整的思路、关键代码和排错经验整理出来给需要快速构建WebSocket应用的同学做个参考。1. 项目概述与整体设计思路1.1 为什么要自研而不是直接用现成IM先聊个实际问题市面上现成的IM方案很多融云、环信、腾讯云IM功能强大但有个共同特点——贵而且重。如果只是需要一个小范围使用的聊天室比如内部技术群、直播间弹幕、在线客服自己用SpringBoot WebSocket攒一个成本极低还能完全掌控数据流和扩展方向。这笔账很好算现成IM按日活收费小项目一个月几百上千一年下来够买台服务器了。自研的话只需要一个能跑SpringBoot的机器连接数在自己掌控范围内想怎么改就怎么改。1.2 项目定位与功能边界这个项目最开始的目标很明确做一个轻量级的在线聊天室支持多人同时在线、消息实时收发、显示聊天记录、处理用户上下线通知。不追求花哨的功能但基础体验要稳。具体功能清单如下用户通过页面输入昵称进入聊天室支持多人实时收发消息系统自动广播用户上线/离线通知支持查看历史聊天记录展示当前在线用户列表支持私聊附带实现思路注意这里的有序列表虽然只有一项但功能边界划定很重要后面所有设计都围绕这个边界展开。再多做就是另一个项目了比如消息持久化、群组管理、消息撤回这些属于IM平台范畴不适合塞进这个轻量级项目里。不要一开始就想着做大而全而是先跑通核心链路稳住基础体验。1.3 技术栈选型的底层逻辑核心框架SpringBoot为什么选它因为SpringBoot极大简化了Spring的配置流程内嵌Tomcat一个jar包就能跑起来。对于聊天室这种场景SpringBoot的自动配置能力和生态支持非常合适团队里随便找个Java开发都能快速上手。通信协议WebSocket聊天的核心是实时双向通信。HTTP是请求-响应模式客户端不发请求服务器就只能干等着实现消息推送需要轮询或长轮询浪费资源且实时性差。WebSocket不一样一次握手建立长连接后服务器可以主动推消息给客户端这是聊天室、弹幕、实时通知这类场景的基础需求。类比一下HTTP就像打电话时要对方先拨过来你才能说话WebSocket就是接通后随时都能说不用管是谁先发起的。前端技术这里用了最朴素的方式——原生HTML JavaScript WebSocket API不引入任何前端框架。原因很简单聊天室的核心逻辑在后端前端只需要维护一个WebSocket连接、渲染消息列表、处理用户输入原生写法足够还省去了构建工具的复杂度。如果后续要扩展到生产环境可以从以下几个方向演进前端引入Vue/React来管理状态消息列表的虚拟滚动优化以及断线重连的自动恢复机制。但那是另一个规模的项目了。2. 核心细节解析与关键代码实现2.1 WebSocket在SpringBoot中的接入方式SpringBoot从1.x版本开始就内置了spring-boot-starter-websocket它对WebSocket协议做了封装使用起来非常简洁。依赖配置dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-websocket/artifactId /dependency注意这个starter不仅包含了WebSocket支持还自带了spring-webmvc和spring-core等基础依赖不需要额外引入。版本号由SpringBoot父级POM统一管理不用自己指定。2.2 WebSocket配置类的正确写法接入WebSocket需要两个组件配置类和处理器类。配置类负责注册WebSocket处理器到特定路径处理器类实现具体的业务逻辑。配置类Configuration public class WebSocketConfig implements WebSocketConfigurer { Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(chatWebSocketHandler(), /chat) .addInterceptors(new ChatHandshakeInterceptor()) .setAllowedOrigins(*); } Bean public WebSocketHandler chatWebSocketHandler() { return new ChatWebSocketHandler(); } }这里有几个关键点需要细说.addInterceptors()握手拦截器。在WebSocket握手阶段拦截请求可以校验用户身份、从HTTP Session中提取用户信息、给WebSocket Session打标签。这个在聊天室场景里很重要因为WebSocket连接建立后服务器需要知道每个连接对应哪个用户。.setAllowedOrigins(*)允许跨域的来源。在开发调试时方便但生产环境一定要设置成实际的域名列表否则任何人都能从别的网站往你的聊天室发消息形成恶意流量。2.3 WebSocket握手拦截器的实现public class ChatHandshakeInterceptor implements HandshakeInterceptor { Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, MapString, Object attributes) throws Exception { if (request instanceof ServletServerHttpRequest) { ServletServerHttpRequest servletRequest (ServletServerHttpRequest) request; String nickname servletRequest.getServletRequest().getParameter(nickname); if (nickname null || nickname.trim().isEmpty()) { return false; } attributes.put(nickname, nickname.trim()); } return true; } Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) { // 握手完成后的回调可用于日志记录 } }注意attributes参数它是握手阶段和WebSocket会话之间传递数据的桥梁。在beforeHandshake里put进去的数据可以在后续的WebSocketHandler中通过WebSocketSession.getAttributes()取出来。这是一个非常关键的机制。用户在页面输入昵称通过URL参数传给后端握手拦截器接收后放进attributes里后续处理器就能知道每个连接是谁。2.4 WebSocket处理器的核心逻辑这是整个聊天室的后端心脏负责处理连接建立、消息收发、断开连接等全部事件。Component public class ChatWebSocketHandler extends TextWebSocketHandler { // 在线用户列表key为sessionIdvalue为nickname private static final MapString, String ONLINE_USERS new ConcurrentHashMap(); // 所有连接的session映射key为sessionId private static final MapString, WebSocketSession SESSION_MAP new ConcurrentHashMap(); Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { String nickname (String) session.getAttributes().get(nickname); ONLINE_USERS.put(session.getId(), nickname); SESSION_MAP.put(session.getId(), session); // 广播上线通知 String message buildSystemMessage(nickname 加入了聊天室); broadcastToAll(message); // 广播在线用户列表 broadcastUserList(); } Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { String nickname ONLINE_USERS.get(session.getId()); String content message.getPayload(); // 判断是否为私聊消息简单约定前缀 if (content.startsWith()) { handlePrivateMessage(session, nickname, content); } else { // 构造聊天消息并广播 ChatMessage chatMessage new ChatMessage(nickname, content, LocalDateTime.now()); broadcastToAll(new TextMessage(JSON.toJSONString(chatMessage))); } } Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { String nickname ONLINE_USERS.remove(session.getId()); SESSION_MAP.remove(session.getId()); if (nickname ! null) { broadcastToAll(buildSystemMessage(nickname 离开了聊天室)); } broadcastUserList(); } private void broadcastToAll(TextMessage message) { for (WebSocketSession session : SESSION_MAP.values()) { if (session.isOpen()) { try { session.sendMessage(message); } catch (IOException e) { // 单点失败不影响其他连接记录日志继续 e.printStackTrace(); } } } } }几个实现要点并发安全使用ConcurrentHashMap维护在线用户因为WebSocket是多线程环境下工作的多个连接的事件由不同线程处理。普通的HashMap在并发修改时会抛异常这个坑我踩过。消息广播遍历所有在线session发送消息。连接数小于1000时这个方案完全够用如果超过这个量级需要考虑分组广播或多节点间的消息转发比如引入Redis Pub/Sub。私聊设计这里用了一个简单约定消息以开头表示私聊格式是目标用户昵称:内容。解析后只发送给目标session。生产环境建议用独立的私聊消息类型在JSON里带上target字段避免歧义。2.5 消息对象设计传输层的消息结构需要简单而清晰。我用了一个统一的JSON结构{ type: chat, nickname: 小明, content: 大家好, timestamp: 2025-01-15 14:30:00 }类型区分为一下几种chat群聊消息nickname为发送人昵称system系统通知比如用户上线、离线userList在线用户列表private私聊消息带target字段标注目标用户对应的Java对象public class ChatMessage { private String type; private String nickname; private String content; private String target; private String timestamp; // getter/setter 省略 }2.6 前端页面实现前端虽然简单但对交互体验的影响很大。核心逻辑就五个部分建立WebSocket连接监听消息事件并渲染发送消息处理重连渲染在线用户列表关键代码片段const ws new WebSocket(ws://${window.location.host}/chat?nickname${encodeURIComponent(nickname)}); ws.onmessage function(event) { const data JSON.parse(event.data); switch (data.type) { case system: appendSystemMessage(data.content); break; case userList: renderUserList(data.content); break; case chat: appendChatMessage(data.nickname, data.content, data.timestamp); break; } }; ws.onclose function() { // 断线重连逻辑5秒后尝试重新连接 setTimeout(connect, 5000); };注意一点ws://和wss://的协议选择。如果网站是HTTPS的WebSocket必须要用wss://否则浏览器会直接拒绝连接。这个在生产环境特别容易踩坑。3. 完整实操过程从零搭建一个可运行的聊天室3.1 环境准备我的开发环境供你参考JDK 8Maven 3.6SpringBoot 2.7.x任意一款IDEA使用SpringBoot 2.7.x是个平衡选择它基于javax包名与SpringBoot 3.x的jakarta包名方案不同生态兼容性更好安全性更新仍在支持期内。如果项目中需要高版本的Java特性可以迁到SpringBoot 3.x代码改动不大主要是导入包的变更。3.2 pom.xml 完整配置?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version relativePath/ /parent groupIdcom.example/groupId artifactIdchat-room/artifactId version1.0.0/version namechat-room/name description轻量级在线聊天室/description properties java.version1.8/java.version /properties dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-websocket/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency /dependencies build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId /plugin /plugins /build /project3.3 项目结构与核心类清单项目结构采用SpringBoot标准布局src/main/java/com/example/chatroom/ ├── ChatRoomApplication.java // 启动类 ├── config/ │ └── WebSocketConfig.java // WebSocket配置类 ├── interceptor/ │ └── ChatHandshakeInterceptor.java // 握手拦截器 ├── handler/ │ └── ChatWebSocketHandler.java // WebSocket处理器 ├── model/ │ ├── ChatMessage.java // 消息实体 │ └── UserInfo.java // 用户信息封装 └── util/ └── MessageUtils.java // 消息构造工具 src/main/resources/ ├── static/ │ ├── index.html // 聊天室页面 │ ├── css/style.css // 样式文件 │ └── js/chat.js // 前端逻辑 └── application.yml // 配置文件3.4 配置文件与启动类application.yml配置server: port: 8080 spring: application: name: chat-room thymeleaf: cache: false这个配置其实不需要额外东西WebSocket相关的参数大部分用默认值即可。如果你需要调整WebSocket传输缓冲区大小可以配置server: tomcat: max-connections: 10000启动类就是标准的SpringBoot入口SpringBootApplication public class ChatRoomApplication { public static void main(String[] args) { SpringApplication.run(ChatRoomApplication.class, args); } }3.5 前端HTML一个页面搞定index.html的核心结构!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title在线聊天室/title link relstylesheet hrefcss/style.css /head body !-- 登录层 -- div idloginPanel input typetext idnicknameInput placeholder请输入昵称 maxlength20 button onclickenterChatRoom()进入聊天室/button /div !-- 聊天主界面 -- div idchatPanel styledisplay: none; div classsidebar h3在线用户 (span iduserCount0/span)/h3 ul iduserList/ul /div div classmain-area div idmessageArea/div div classinput-area textarea idmessageInput rows3/textarea button onclicksendMessage()发送/button /div /div /div script srcjs/chat.js/script /body /html界面布局做成左右结构左侧用户列表右侧消息区和输入框。这样虽然简陋但信息层级一目了然。实际使用时可以根据UI需求换肤。3.6 前端JavaScript的完整逻辑let ws null; let currentNickname ; function enterChatRoom() { const nickname document.getElementById(nicknameInput).value.trim(); if (!nickname) { alert(请输入昵称); return; } if (nickname.length 20) { alert(昵称过长请控制在10个字符以内); return; } currentNickname nickname; connect(); } function connect() { const scheme window.location.protocol https: ? wss:// : ws://; ws new WebSocket(${scheme}${window.location.host}/chat?nickname${encodeURIComponent(currentNickname)}); ws.onopen function() { document.getElementById(loginPanel).style.display none; document.getElementById(chatPanel).style.display flex; appendSystemMessage(连接成功开始聊天吧); }; ws.onmessage function(event) { const data JSON.parse(event.data); handleMessage(data); }; ws.onclose function(event) { appendSystemMessage(连接已断开5秒后尝试重连...); setTimeout(connect, 5000); }; ws.onerror function(error) { console.error(WebSocket错误:, error); }; } function handleMessage(data) { switch (data.type) { case system: appendSystemMessage(data.content); break; case chat: appendChatMessage(data.nickname, data.content, data.timestamp); break; case themeChange: document.body.classList.add(dark-mode); break; default: break; } } function sendMessage() { const input document.getElementById(messageInput); const content input.value.trim(); if (!content) return; if (ws ws.readyState WebSocket.OPEN) { ws.send(content); input.value ; } else { appendSystemMessage(消息发送失败连接未建立); } } function appendChatMessage(nickname, content, timestamp) { const messageArea document.getElementById(messageArea); const div document.createElement(div); div.className message; div.innerHTML span classnickname${escapeHtml(nickname)}/span span classtime${formatTime(timestamp)}/span div classcontent${escapeHtml(content)}/div; messageArea.appendChild(div); messageArea.scrollTop messageArea.scrollHeight; } function appendSystemMessage(content) { const messageArea document.getElementById(messageArea); const div document.createElement(div); div.className system-message; div.textContent content; messageArea.appendChild(div); messageArea.scrollTop messageArea.scrollHeight; } function escapeHtml(text) { const div document.createElement(div); div.appendChild(document.createTextNode(text)); return div.innerHTML; }这里重点推荐一下escapeHtml这个函数它是防XSS攻击的关键。如果直接使用innerHTML拼接用户输入用户发一个script标签就能劫持页面轻则弹广告重则窃取登录信息。用textContent或转义函数处理就能避免这个风险。有一点要小心不要在handleMessage里做太多事情。这里删掉了重复的userList处理分支因为在线用户列表的渲染逻辑放在单独的函数里避免大块逻辑堆在一起。3.7 编译运行与本地联调编译和启动mvn clean package -DskipTests java -jar target/chat-room-1.0.0.jar启动成功后浏览器访问http://localhost:8080输入昵称进入聊天室。建议开两个浏览器窗口一个用Chrome一个用Firefox模拟两个用户验证消息收发是否正常。注意本地调试时如果开两个标签页登录同一浏览器因为Session Cookie的共享可能出现串用户的问题。测试时建议使用浏览器的隐身窗口或多个浏览器确保会话隔离。3.8 Docker部署方案一个轻量级服务部署打包成Docker镜像后在任意环境拉起都很方便。FROM openjdk:8-jre-alpine COPY target/chat-room-1.0.0.jar /app.jar EXPOSE 8080 ENTRYPOINT [java, -jar, /app.jar]构建与运行docker build -t chat-room . docker run -d --name chat-room -p 8080:8080 chat-room如果有多台机器部署准备用Nginx做负载均衡需要在Nginx配置里增加WebSocket转发的长连接支持关键配置如下location /chat { proxy_pass http://backend_server; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; proxy_read_timeout 3600s; proxy_send_timeout 3600s; }WebSocket首次通过HTTP升级协议建立连接Upgrade和Connection头是关键。proxy_read_timeout设长是因为聊天场景下连接长时间不通信是常态默认60秒超时会导致连接被Nginx掐断前端的重连机制就会频发触发。4. 常见问题与排查技巧实录4.1 无法建立WebSocket连接浏览器控制台报402错误这个问题是重灾区。排查思路遵循以下顺序访问路径错了确认WebSocket地址的路径和registerWebSocketHandlers注册的路径完全一致包括大小写和斜杠。协议不匹配HTTPS页面必用wss://HTTP页面用ws://混用必然失败。中间代理拦截生产环境经过Nginx或CDN时必须配置Upgrade头透传。跨域限制服务端setAllowedOrigins(*)在开发环境下放行了所有来源生产环境要改为具体域名。大多数情况下先改协议前缀和路径就能解决。4.2 连接总是自动断开1006异常用浏览器调试时看到WebSocket connection closed或状态码1006表示连接以一种非正常的机制被关闭。常见原因Nginx代理超时设置过短默认60秒会掐断空闲连接。解决方案是增加proxy_read_timeout并开启心跳保活。服务器防火墙或云平台安全组限制长连接需要确认TCP连接空闲timeout策略。服务端处理消息时抛异常导致连接被强制关闭。检查服务端日志中的MAX_Message_size相关报错超出缓冲区大小也会断连。在客户端增加心跳机制能有效减少这类问题setInterval(() { if (ws ws.readyState WebSocket.OPEN) { ws.send(JSON.stringify({ type: ping, content: {type:ping,timestamp: new Date().toISOString() } })); } }, 30000);服务端收到ping类型消息时忽略内容即可客户端依赖任何网络往返来保活和探活。注意心跳的JSON要转义好避免服务端解析报错。4.3 消息发送乱码乱码通常出现在WebSocket握手阶段传中文昵称时。URL参数传递中文必须做encodeURIComponent编码。服务端接收参数时要确保使用UTF-8解码。SpringBoot默认使用UTF-8但如果自定义了Servlet过滤器或前置代理可能在链路的某个环节被改成了ISO-8859-1。排查方式先在前端console.log输出编码前后的字符串再在拦截器里打印收到的参数对比两步就能定位问题在哪一层。4.4 消息丢失或前端偶发不显示消息丢失有两种情况广播时个别连接未遍历到session.sendMessage()不能在多个线程同时对一个session调用否则会抛异常导致消息丢失。单个连接上消息加锁或用ConcurrentWebSocketSessionDecorator包装session能解决并发发送的冲突问题。前端渲染顺序错乱不同的消息到达时间不同如果前端没有按时间戳排序离线消息和实时消息混在一起会显得乱。前端做一层消息缓冲每次渲染前按时间戳做一次排序就可以。4.5 高并发下的性能瓶颈当在线人数上升到一定量级单台服务器单进程的广播方式会遇到瓶颈CPU飙升每次广播都是全量遍历O(n)复杂度。人数上万时每条消息都会触发上万次发送。内存溢出ConcurrentHashMap存session引用的方式每个连接占用一定内存需要设置最大连接数上限。优化路径按顺序走同机器内使用线程池异步广播避免阻塞WebSocket工作线程。引入Redis Pub/Sub进行多节点广播实现水平扩展。改为Netty替换Tomcat容器提升单机连接上限。前端做流式渲染使用requestAnimationFrame批处理消息追加。这个项目的定位是轻量级正常几百人以内完全不需要这些优化但如果目标是万人级别的直播弹幕场景这些方向值得参考。4.6 生产环境必须要做的四件事这里记录一下部署到生产环境时容易忽略的事情配置HTTPS和WSS这个身份认证时代的标配。服务端是SpringBoot时可以通过server.ssl.*配置证书或者更常见的是在Nginx层终止SSL再把WSS流量转发到后端的WS端口。限制Origin收到WebSocket握手请求时校验Origin头是否在白名单内防止其他网站恶意连接你的WebSocket服务。setAllowedOrigins正是为这个场景服务的。日志记录与监控关键事件连接、断开、异常要打日志方便事后追溯。消息频率限制如果不加限制一个用户每秒发500条消息就能打爆整个聊天室。可以在服务端做一个简单的限流器比如IP用户维度每秒最多5条。5. 源码结构与文档说明5.1 源码目录说明这个项目的源码已经按Maven标准结构整理好目录规划遵循了常见的分层约定方便在IDEA或Eclipse中直接打开。chat-room/ ├── src/ │ ├── main/ │ │ ├── java/com/example/chatroom/ │ │ │ ├── ChatRoomApplication.java // SpringBoot启动入口 │ │ │ ├── config/ │ │ │ │ └── WebSocketConfig.java // WebSocket配置中心 │ │ │ ├── interceptor/ │ │ │ │ └── ChatHandshakeInterceptor.java // 握手拦截器 │ │ │ ├── handler/ │ │ │ │ └── ChatWebSocketHandler.java // 核心消息处理器 │ │ │ ├── model/ │ │ │ │ ├── ChatMessage.java // 消息实体 │ │ │ │ └── UserInfo.java // 用户实体 │ │ │ └── util/ │ │ │ └── MessageUtils.java // 消息构造工具类 │ │ └── resources/ │ │ ├── static/ │ │ │ ├── index.html // 前端聊天页面 │ │ │ ├── css/style.css // 样式文件 │ │ │ └── js/chat.js // 前端逻辑脚本 │ │ └── application.yml // 配置文件 │ └── test/ │ └── java/com/example/chatroom/ │ └── ChatRoomApplicationTests.java // 测试类 ├── pom.xml └── README.md5.2 项目文档说明与快上手技巧README.md 里记录了以下几部分核心文档项目简介和功能清单环境要求JDK版本、Maven版本、SpringBoot版本快速启动步骤配置文件说明支持的业务流程和消息类型说明常见问题FAQ后续功能迭代方向这里给出一个快速上手的建议拿到源码后不要急着读全部代码先按README里的启动步骤把项目跑起来。在浏览器里体验完整个聊天流程后再带着问题去读源码理解效率会高很多。上手核心三问连接是怎么建立的—— 看配置类和拦截器消息是怎么流转的—— 看处理器的三个核心方法前端是怎么接线的—— 看chat.js的connect和sendMessage5.3 如何基于这个项目做二次开发这个项目最大的价值在于它是一个可运行的起点适合在此基础上快速扩展业务。这里提供几个高频定制维度的思路列表消息持久化引入Spring Data JPA或MyBatis在handleTextMessage中保存消息到数据库。用户认证把URL传参方式替换为Spring Security或JWT认证从登录凭证中解析用户信息。表情与图片发送内容支持URL预览和图片标签渲染后端增加上传接口。敏感词过滤引入sensitive-word库在广播前做消息内容过滤。房间机制在attributes中增加房间ID字段ONLINE_USERS改为Map房间ID, MapsessionId, nickname。主题换肤前端增加CSS变量提供明亮/暗黑主题切换。每次扩展时先改消息协议结构再改服务端处理逻辑最后调整前端渲染按照这个顺序推进最顺畅。5.4 使用感想与踩坑心得最后分享一点个人体会。WebSocket聊天室是个很好的练手项目它麻雀虽小五脏俱全涉及了TCP长连接、并发编程、消息协议设计、前后端联调、网络代理配置等知识点。我实际做过之后最大的感受是这类实时应用的难点不在写代码而在网络环境的不确定性。你本机跑得好好的一旦部署到云服务器各种Nginx超时、防火墙断连、跨域限制就冒出来了。做一个能用的聊天室半天就够了要做一个在各种极端网络环境下都能稳定工作的聊天室才真正考验功底。如果你准备拿这个项目练手或者做毕业设计建议先保证基础功能跑通再把重连机制、心跳检测、主动退房这几件“防御性功能”做好整个项目的完成度会有一个质的提升。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价