大概目录结构
依赖只引入了JSP 和SpringBoot整合WebSocket Spring Web
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script src="${pageContext.request.contextPath}/js/jquery.min.js"></script> <title>SpringBoot+WebSocket+JSP</title> </head> <body style="margin: 45px;"> <h4>在线聊天室</h4> <div class="form-group"> <label for="content"></label> <textarea id="content" readonly="readonly" cols="80" rows="15"></textarea> </div> <div class="form-group" style="margin-top: 8px"> <textarea id="message" cols="80" rows="5" placeholder="请输入消息"></textarea> <div style="margin-top: 10px"> <button id="toSend" class="btn btn-info">发送</button> <button id="user_exit" class="btn btn-danger">离开</button> <input id="username" value="${username}" style="display: none"> </div> </div> <script type="text/javascript"> $(function () { var ws; if ("WebSocket" in window) { var baseUrl = 'ws://localhost:8080/websocket/'; var userName = $('#username').val(); ws = new WebSocket(baseUrl + userName); // 连通之后的回调事件,建立连接 ws.onopen = function () { console.log("建立 websocket 连接..."); }; // 接收后台服务端的消息 ws.onmessage = function (event) { $('#content').append(event.data + '\n\n'); console.log("接收到服务端发送的消息..." + event.data + '\n'); }; ws.onerror = function (event) { console.log("websocket发生错误..." + event + '\n'); } // 连接关闭的回调事件 ws.onclose = function () { $('#content').append('[' + userName + '] 已离开!'); console.log("关闭 websocket 连接..."); }; } else { // 浏览器不支持 WebSocket alert("您的浏览器不支持WebSocket!"); } // 客户端发送消息到服务器 $('#toSend').click(function () { sendMsg(); }); $(document).keyup(function (event) { // 回车键事件 if (event.keyCode == 13) { sendMsg(); } }); // 发送消息 function sendMsg() { //websocket发送消息 ws.send($('#message').val()); $('#message').val(""); } // 退出 $('#user_exit').click(function () { if (ws) { ws.close(); } }); }); </script> </body> </html>
控制器
@Controller public class ChatController { private AtomicInteger idProducer = new AtomicInteger(); @RequestMapping("/") public String index(Model model) { model.addAttribute("username","user" + idProducer.getAndIncrement()); return "index"; } }
配置类
@EnableWebSocket //启用WebSocket支持 @Configuration //表示配置类 public class WebSocketConfig { /** * 配置ServerEndpointExporter的bean * * 该Bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint */ @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } }
/** * 说明: * 1、@ServerEndpoint注解中指定WebSocket协议的地址; * 2、@OnOpen、@OnMessage、@OnClose、@OnError注解与WebSocket中监听事件对应 * **/ @Slf4j //lombok jar包,帮我们自动生成一些代码:@Data @Component @ServerEndpoint("/websocket/{username}") public class ChatServerEndpoint { /** * 连接建立时触发 */ @OnOpen public void openSession(@PathParam("username") String username, Session session) { log.info("用户{}登录", username); String message = "用户[" + username + "] 已进入聊天室!"; // 发送登录消息给其他人 WebSocketUtils.sendMessageAll(message); // 获取当前在线人数,发给自己 String onlineInfo = WebSocketUtils.getOnlineInfo(); //发送消息 WebSocketUtils.sendMessage(session, onlineInfo); // 添加自己到map中 WebSocketUtils.CLIENTS.put(username, session); } /** * 客户端接收服务端数据时触发 */ @OnMessage public void onMessage(@PathParam("username") String username, String message) { log.info("发送消息:{}, {}", username, message); //广播,把消息同步给其他客户端 WebSocketUtils.sendMessageAll("[" + username + "] : " + message); } /** * 连接关闭时触发 */ @OnClose public void onClose(@PathParam("username") String username, Session session) { // 当前的Session移除某个用户 WebSocketUtils.CLIENTS.remove(username); // 离开消息通知所有人 WebSocketUtils.sendMessageAll("[" + username + "] 已离开!"); try { //关闭WebSocket Session会话 session.close(); log.info("{} 已退出, onclose", username); } catch (IOException e) { e.printStackTrace(); log.error("onClose error", e); } } /** * 通信发生错误时触发 */ @OnError public void onError(Session session, Throwable throwable) { try { //关闭WebSocket Session会话 session.close(); } catch (IOException e) { e.printStackTrace(); log.error("onError Exception", e); } log.info("Throwable msg " + throwable.getMessage()); } }
public final class WebSocketUtils { private static final Logger logger = LoggerFactory.getLogger(WebSocketUtils.class); /** * 存储WebSocket session * <p> * 用户名为key,WebSocket Session对象为value */ public static final Map<String, Session> CLIENTS = new ConcurrentHashMap<>(); /** * 使用连接发送数据 * * @param session 用户session * @param message 发送内容 */ public static void sendMessage(Session session, String message) { if (session == null) { return; } final RemoteEndpoint.Basic basic = session.getBasicRemote(); if (basic == null) { return; } try { //发送 basic.sendText(message); } catch (IOException e) { e.printStackTrace(); logger.error("sendMessage IOException ", e); } } /** * 发送消息给其他所有人 * * @param message */ public static void sendMessageAll(String message) { CLIENTS.forEach((sessionId, session) -> sendMessage(session, message)); } /** * 获取所有在线用户 */ public static String getOnlineInfo() { Set<String> userNames = CLIENTS.keySet(); if (userNames.size() == 0) { return "当前无人在线..."; } return CLIENTS.keySet().toString() + "在线"; } }