当前位置: 首页 > news >正文

springboot 实现websocket通信

一、环境准备:
添加maven依赖

   <!-- websocket包 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency><!-- 通用工具 --><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.25</version></dependency>

二、前端代码

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>DeepSeek WebSocket客户端</title><style>:root {--primary: #4b6cb7;--secondary: #182848;--accent: #36D1DC;--light: #f8f9fa;--dark: #343a40;--success: #28a745;--danger: #dc3545;--warning: #ffc107;}* {margin: 0;padding: 0;box-sizing: border-box;font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;}body {background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);color: #333;min-height: 100vh;padding: 20px;display: flex;flex-direction: column;align-items: center;}.container {width: 100%;max-width: 1000px;background: rgba(255, 255, 255, 0.95);border-radius: 15px;box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);overflow: hidden;margin: 20px 0;display: flex;flex-direction: column;}header {background: linear-gradient(90deg, var(--primary), var(--secondary));color: white;padding: 20px;text-align: center;}h1 {font-size: 2.2rem;margin-bottom: 10px;}.description {font-size: 1.1rem;opacity: 0.9;margin-bottom: 15px;}.api-info {background: rgba(0, 0, 0, 0.2);padding: 10px;border-radius: 8px;font-family: monospace;word-break: break-all;margin-top: 10px;font-size: 0.9rem;}.main-content {display: flex;flex-direction: row;height: 500px;}@media (max-width: 768px) {.main-content {flex-direction: column;height: auto;}}.chat-container {flex: 3;display: flex;flex-direction: column;padding: 20px;border-right: 1px solid #eee;}.chat-messages {flex: 1;overflow-y: auto;padding: 15px;background: #f9f9f9;border-radius: 10px;margin-bottom: 15px;}.message {margin-bottom: 15px;padding: 12px;border-radius: 10px;max-width: 80%;line-height: 1.5;}.user-message {background: #e3f2fd;margin-left: auto;border-bottom-right-radius: 3px;}.ai-message {background: #f5f5f5;margin-right: auto;border-bottom-left-radius: 3px;}.streaming-message {background: #e8f5e9;border-left: 4px solid var(--success);}.input-area {display: flex;gap: 10px;}input {flex: 1;padding: 12px 15px;border: 1px solid #ddd;border-radius: 25px;font-size: 1rem;outline: none;transition: border-color 0.3s;}input:focus {border-color: var(--primary);}button {background: var(--primary);color: white;border: none;border-radius: 25px;padding: 12px 25px;cursor: pointer;font-size: 1rem;font-weight: 600;transition: background 0.3s;}button:hover {background: var(--secondary);}button:disabled {background: #cccccc;cursor: not-allowed;}.sidebar {flex: 1;padding: 20px;background: #f5f5f5;display: flex;flex-direction: column;}.connection-panel {margin-bottom: 20px;}.control-group {margin-bottom: 15px;}label {display: block;font-weight: 600;margin-bottom: 5px;font-size: 0.9rem;}select, input[type="text"] {width: 100%;padding: 10px;border-radius: 6px;border: 1px solid #ddd;}.status-panel {margin-top: auto;padding: 15px;background: white;border-radius: 10px;box-shadow: 0 2px 5px rgba(0,0,0,0.1);}.status-item {display: flex;justify-content: space-between;margin-bottom: 10px;}.status-value {font-weight: 600;}.connected {color: var(--success);}.disconnected {color: var(--danger);}.connecting {color: var(--warning);}.code {font-family: 'Courier New', monospace;background: #2d2d2d;color: #f8f8f2;padding: 10px;border-radius: 5px;overflow-x: auto;margin: 10px 0;font-size: 0.9rem;}.message-time {font-size: 0.7rem;color: #777;text-align: right;margin-top: 5px;}.btn-secondary {background: #5a6268;padding: 8px 15px;font-size: 0.9rem;}.btn-secondary:hover {background: #4e555b;}.btn-danger {background: var(--danger);}.btn-danger:hover {background: #bd2130;}</style>
</head>
<body>
<div class="container"><header><h1>DeepSeek WebSocket客户端</h1><p class="description">通过WebSocket实现与DeepSeek API的流式通信</p><div class="api-info">模型: /deepseek-ai/DeepSeek-R1-Distill-Qwen-32B | 传输: WebSocket</div></header><div class="main-content"><div class="chat-container"><div class="chat-messages" id="chatMessages"><div class="message ai-message"><p>欢迎使用DeepSeek WebSocket客户端!请先连接到服务器,然后开始对话。</p><div class="message-time" id="messageTime">系统消息</div></div></div><div class="input-area"><input type="text" id="userInput" placeholder="输入您的问题..." autocomplete="off" disabled><button id="sendButton" disabled>发送</button></div></div><div class="sidebar"><div class="connection-panel"><h3>连接设置</h3><div class="control-group"><label for="serverUrl">WebSocket服务器</label><input type="text" id="serverUrl" value="wss://127.0.0.1:90/imServer/zwj"></div><div class="control-group"><button id="connectButton">连接</button><button id="disconnectButton" class="btn-danger" disabled>断开</button></div></div><div class="settings-panel"><h3>设置</h3><div class="control-group"><label for="streamOption">响应类型</label><select id="streamOption"><option value="stream">流式响应 (实时显示)</option><option value="complete">完整响应</option></select></div><div class="control-group"><button id="clearChat" class="btn-secondary">清除对话</button></div></div><div class="status-panel"><h3>连接状态</h3><div class="status-item"><span>状态:</span><span class="status-value" id="connectionStatus">未连接</span></div><div class="status-item"><span>消息数:</span><span class="status-value" id="messageCount">0</span></div><div class="status-item"><span>最后活动:</span><span class="status-value" id="lastActivity">-</span></div></div></div></div>
</div><script>document.addEventListener('DOMContentLoaded', function() {// DOM元素const chatMessages = document.getElementById('chatMessages');const userInput = document.getElementById('userInput');const sendButton = document.getElementById('sendButton');const connectButton = document.getElementById('connectButton');const disconnectButton = document.getElementById('disconnectButton');const clearChatButton = document.getElementById('clearChat');const connectionStatus = document.getElementById('connectionStatus');const messageCount = document.getElementById('messageCount');const lastActivity = document.getElementById('lastActivity');const serverUrl = document.getElementById('serverUrl');// 状态变量let ws = null;let isConnected = false;let msgCount = 0;let currentAiMessageElement = null;// 格式化时间function formatTime(date = new Date()) {return date.toLocaleTimeString('zh-CN', {hour: '2-digit',minute: '2-digit',second: '2-digit'});}// 更新状态显示function updateStatus(status, className) {connectionStatus.textContent = status;connectionStatus.className = 'status-value ' + className;lastActivity.textContent = formatTime();}// 添加消息到聊天窗口function addMessage(content, isUser = false, isStreaming = false) {const messageElement = document.createElement('div');messageElement.classList.add('message');messageElement.classList.add(isUser ? 'user-message' : 'ai-message');if (isStreaming) {messageElement.classList.add('streaming-message');currentAiMessageElement = messageElement;}messageElement.innerHTML = `<p>${content}</p><div class="message-time">${isUser ? '您' : 'DeepSeek'} · ${formatTime()}</div>`;chatMessages.appendChild(messageElement);chatMessages.scrollTop = chatMessages.scrollHeight;// 更新消息计数msgCount++;messageCount.textContent = msgCount;return messageElement;}// 更新消息内容function updateMessage(element, newContent) {element.querySelector('p').textContent = newContent;element.querySelector('.message-time').textContent = `DeepSeek · ${formatTime()} (更新中)`;chatMessages.scrollTop = chatMessages.scrollHeight;}// 连接到WebSocket服务器function connect() {if (isConnected) return;try {updateStatus('连接中...', 'connecting');// 创建WebSocket连接ws = new WebSocket(serverUrl.value);ws.onopen = function() {isConnected = true;updateStatus('已连接', 'connected');userInput.disabled = false;sendButton.disabled = false;connectButton.disabled = true;disconnectButton.disabled = false;addMessage('已成功连接到DeepSeek服务器', false);};ws.onmessage = function(event) {debuggerconst data = JSON.parse(event.data);if (data.type === 'response') {if (data.is_complete) {// 响应完成if (currentAiMessageElement) {currentAiMessageElement.classList.remove('streaming-message');currentAiMessageElement = null;}} else if (currentAiMessageElement) {// 更新流式响应updateMessage(currentAiMessageElement, data.content);} else {// 新响应addMessage(data.content, false, true);}} else if (data.type === 'error') {addMessage(`错误: ${data.message}`, false);} else if (data.type === 'info') {addMessage(`系统: ${data.message}`, false);}};ws.onclose = function() {isConnected = false;updateStatus('已断开', 'disconnected');userInput.disabled = true;sendButton.disabled = true;connectButton.disabled = false;disconnectButton.disabled = true;addMessage('与服务器的连接已断开', false);};ws.onerror = function(error) {addMessage(`连接错误: ${error}`, false);updateStatus('连接错误', 'disconnected');};} catch (error) {addMessage(`连接异常: ${error}`, false);updateStatus('连接异常', 'disconnected');}}// 断开WebSocket连接function disconnect() {if (ws) {ws.close();ws = null;}}// 发送消息function sendMessage() {if (!isConnected || !ws) {addMessage('错误: 未连接到服务器', false);return;}const message = userInput.value.trim();if (!message) return;// 添加用户消息到聊天addMessage(message, true);// 清空输入框userInput.value = '';// 准备AI消息元素用于流式响应if (document.getElementById('streamOption').value === 'stream') {addMessage('思考中...', false, true);}// 通过WebSocket发送消息try {ws.send(JSON.stringify({type: 'query',content: message,stream: document.getElementById('streamOption').value === 'stream'}));} catch (error) {addMessage(`发送错误: ${error}`, false);}}// 清除聊天记录function clearChat() {chatMessages.innerHTML = '';msgCount = 0;messageCount.textContent = '0';addMessage('聊天记录已清除', false);}// 事件监听器connectButton.addEventListener('click', connect);disconnectButton.addEventListener('click', disconnect);sendButton.addEventListener('click', sendMessage);clearChatButton.addEventListener('click', clearChat);userInput.addEventListener('keypress', function(e) {if (e.key === 'Enter') {sendMessage();}});// 初始化addMessage('WebSocket客户端已就绪,请点击"连接"按钮开始对话。', false);});
</script>
</body>
</html>

三、后端代码

 
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.alibaba.druid.support.json.JSONUtils;
import com.alibaba.fastjson.JSONObject;
import jakarta.websocket.OnClose;
import jakarta.websocket.OnError;
import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;@ServerEndpoint(value = "/imServer/{username}")
@Component
public class WebSocketServer {private  static final Logger logs = LoggerFactory.getLogger(WebSocketServer.class);public static  final Map<String, Session> sessionMap = new ConcurrentHashMap<>();/*** 创建链接* @param session*/@OnOpenpublic void onOpen(Session session, @PathParam("username") String username){sessionMap.put(username,session);logs.info("新用户加入username:{},当前人数:{}",username,sessionMap.size());JSONObject result = new JSONObject();JSONArray array = new JSONArray();result.put("users",array);for(Object key :sessionMap.keySet()){JSONObject jsonObject = new JSONObject();jsonObject.put("username",key);array.add(jsonObject);}sedAllMessage(JSONUtils.toJSONString(result));//把当前用户列表返给客户端}/*** 关闭连接* @param session* @param username 指定关闭哪一个session连接*/@OnClosepublic void onClose(Session session, @PathParam("username") String username){sessionMap.remove(username);logs.info(username+"连接关闭成功");}/*** 消息中转站* 接收到客户端消息后调用此方法* @param message 客户端发来的消息* @param username 用户名*/@OnMessagepublic void onMessage(String message, @PathParam("username") String username){logs.info("服务端收到用户{}的消息:{}",username,message);cn.hutool.json.JSONObject parse = JSONUtil.parseObj(message) ;String toUserName = parse.getStr("to"); // 要发给谁String text = parse.getStr("content");  // 发什么信息Session toSession = sessionMap.get(username); // 找到对应用户的Sessionif(toSession != null){JSONObject json = new JSONObject();json.put("from",username);json.put("type","response");json.put("content","回答:"+text);this.sendMessage(toSession,json.toJSONString());logs.info("发送到用户:{},消息:{}",toUserName,json.toJSONString());}else {logs.info("发送失败,未找到用户:{}的session连接",toUserName);}}@OnErrorpublic void onError(Throwable error){logs.error("webSocket 服务异常"+error.getMessage());error.printStackTrace();}/*** 把消息发给所有客户端* @param message*/private void sedAllMessage(String message){try {for(Session session:sessionMap.values()){logs.info("服务端给客户端[{}]发送信息{}",session.getId(),message);session.getBasicRemote().sendText(message);}} catch (Exception e) {logs.error("服务端发送消息到客户端失败",e);}}/*** 向指定用户的session连接中发送消息* @param session 指定用户的session* @param message 消息文本*/private void sendMessage(Session session,String message){try {session.getBasicRemote().sendText(message);//向客户端发送信息} catch (Exception e) {logs.error("服务端发送消息到客户端失败",e);}}}

四、如果有shrio权限,设置可访问权限

 filterChainDefinitionMap.put("/imServer/**", "anon,captchaValidate");

五、执行连接开始对话
注意:wss:服务器是https,ws:服务器是http方式

效果展示:
在这里插入图片描述

http://www.dtcms.com/a/486641.html

相关文章:

  • 深度学习实战:python动物识别分类检测系统 计算机视觉 Django框架 CNN算法 深度学习 卷积神经网络 TensorFlow 毕业设计(建议收藏)✅
  • app使用什么做的网站wordpress自动保存编辑器图片
  • 静态网页发布到wordpress河南关键词优化搜索
  • 从递归到迭代吃透树的层次——力扣104.二叉树的最大深度
  • 基于无监督深度学习方法的非迭代式、不确定性感知的磁共振成像肝脏脂肪定量评估|文献速递-文献分享
  • 如何能把网站做的更大宿主选择 网站建设
  • django rest framework:从零开始搭建RESTful API
  • springboot3加密配置文件的值
  • 抗干扰汽车微型网络(RAMN)开源测试平台的设计
  • wordpress 评测谷歌seo优化什么意思
  • C++手撕无锁线程池
  • kettle Spoon.bat启动报错:could not create the java virtual machine
  • 连云港做网站企业网站建设简单合同
  • DB-GPT AWEL工作流引擎深度解析
  • 提升应用性能:Go中的同步与异步处理
  • OpenAI 最新开源模型 gpt-oss 架构与训练解析
  • 网站片区韶关网站seo
  • 红帽Linux复习-从命令行管理文件、创建查看编辑文本文件
  • C++11(lambda和包装器)
  • 时钟特性约束(四)
  • 免费公司网站如何建立设计品牌网页设计
  • jQuery 尺寸
  • 做木工网站天河建设网站公司排名
  • 嵌入式软件--STM32 SPI通信(上)
  • PHP “真异步“ TrueAsync SAPI 与 NGINX Unit 集成
  • ARCGIS国土超级工具集1.7更新说明
  • 网站如何做信息表美图在线图片编辑器
  • 分布式专题——45 ElasticSearch基础数据管理详解
  • SQLBot:基于大模型与 RAG 的智能问数系统架构
  • 网站系统维护要多久福清市建设工程交易网站好像