WebSocket资料参考:
https://www.jianshu.com/p/d79bf8174196
使用SpringBoot整合参考:
https://blog.csdn.net/KeepStruggling/article/details/105543449
后端部分:
直接使用Springboot,依赖只有内嵌tomcat和对应的websocket封装启动包
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.3.RELEASE</version> </parent> <groupId>cn.cloud9</groupId> <artifactId>WebSocket</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <!-- 内嵌Tomcat库来提供WebSocketAPI --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring对WebSocket的扩展Starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> </dependencies> </project>
定义WebSocket接口
package cn.cloud9.endpoint; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import javax.websocket.*; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * @author OnCloud9 * @description * @project WebSocket * @date 2022年06月15日 20:13 * * ws://localhost:8080/ws/test */ @Component @ServerEndpoint("/ws/test") public class TestEndpoint { private static final Logger LOGGER = LoggerFactory.getLogger(TestEndpoint.class); private static final Map<String, Session> CLIENT_SESSION_MAP = new ConcurrentHashMap<>(); /** * 侦测客户端向此服务建立连接,此终端实例会新创建出来 * @param session */ @OnOpen public void onOpen(Session session) { LOGGER.info("客户端 {} 开启了连接", session.getId()); // 默认按照ID保管存放这个客户端的会话信息 CLIENT_SESSION_MAP.put(session.getId(), session); } /** * 侦测客户端异常 * @param session * @param error */ @OnError public void onError(Session session, Throwable error) { LOGGER.info("客户端 {} 连接异常... 异常信息:{}", session.getId(), error.getMessage()); LOGGER.error(error.getMessage()); } /** * 侦测客户端向此服务端发送消息 * @param session * @param message */ @OnMessage public void onMessage(Session session, String message) { // 可根据会话ID或者message中自定义唯一标识从容器中取出会话对象来进行操作 LOGGER.info("收到消息, 来自客户端 {}, 消息内容 -> {}", session.getId(), message); } /** * 侦测客户端关闭事件 * @param session */ @OnClose public void onClose(Session session) { LOGGER.info("客户端 {} 关闭了连接...", session.getId()); // 客户端关闭时,从保管容器中踢出会话 CLIENT_SESSION_MAP.remove(session.getId()); } /** * 给所有客户端发送消息 * @param message */ public static void sendMessageForAllClient(String message) { CLIENT_SESSION_MAP.values().forEach(session -> { try { LOGGER.info("给客户端 {} 发送消息 消息内容: {}", session.getId(), message); session.getBasicRemote().sendText(message); } catch (IOException e) { e.printStackTrace(); LOGGER.info("给客户端 {} 发送消息失败, 异常信息:{}", session.getId(), e.getMessage()); } }); } }
配置WebSocket接口暴露器
package cn.cloud9.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.server.standard.ServerEndpointExporter; /** * @author OnCloud9 * @description * @project WebSocket * @date 2022年06月15日 20:42 */ @Configuration public class WebSocketConfig { /** * 如果使用Springboot默认内置的tomcat容器,则必须注入ServerEndpoint的bean; * 如果使用外置的web容器,则不需要提供ServerEndpointExporter,下面的注入可以注解掉 */ @Bean public ServerEndpointExporter serverEndpointExporter(){ return new ServerEndpointExporter(); } }
写一个Controller,通过一般Http接口向WebSocket客户端推送消息
package cn.cloud9.controller; import cn.cloud9.endpoint.TestEndpoint; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * @author OnCloud9 * @description * @project WebSocket * @date 2022年06月15日 20:46 */ @RestController @RequestMapping("/api/ws") public class WebSocketController { /** * http://localhost:8080/api/ws/send * @param message * @return */ @GetMapping("/send") public boolean send(@RequestParam String message) { TestEndpoint.sendMessageForAllClient(message); return true; } }
前端页面:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>WebSocket 页面客户端</title> </head> <body> <div> <button onclick="initConnection()">开启WebSocket连接</button> </div> <div> <input type="text" id="message" ><button onclick="sendMessageFromClient()">发送消息</button> </div> <div> <button onclick="closeConnection()">关闭连接</button> </div> <script> const connectionUrl = 'ws://localhost:8080/ws/test' var webSocket = null function initConnection() { webSocket = new WebSocket(connectionUrl) webSocket.onopen = (event) => { console.log('建立新的WebSocket连接') } webSocket.onmessage = (event) => { const message = JSON.stringify(event.data) console.log(`收到服务端发送的消息,消息内容 -> ${message}`) } webSocket.onerror = (error) => { console.log(`WebSocket连接异常 -> ${JSON.stringify(error)}`) } webSocket.onclose = (event) => { console.log(`WebSocket连接关闭 -> ${JSON.stringify(event)}`) } } function sendMessageFromClient() { const message = document.querySelector('#message').value webSocket.send(JSON.stringify({ message: message })) } function closeConnection() { webSocket.close() } </script> </body> </html>