SpringBoot 集成 WebSocket(1)https://developer.aliyun.com/article/1534233
3.2、实现
3.2.1、编写前端界面和通信逻辑
这里我们通过 js 实现了当用户点击发送按钮的时候,把文本框的内容通过 websocket 发送给指定的地址,我们的后台 websocket 程序收到后会全部群发给所有当前连接的客户端:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>WebSocket Client</title> </head> <script> // 客户端和服务器连接的地址(我们服务端监听的地址) let ws = new WebSocket("ws://localhost:8080/myWs1") ws.onopen=function (){ // 连接打开的时候向服务器发送一条消息 // ws.send("hey man") } ws.onmessage=function (message) { // console.log(message.data) document.getElementById("talkMsg").innerHTML = message.data } function sendMsg(){ // 发送文本框的信息 ws.send(document.getElementById("message").value) // 发送完清空输入框 document.getElementById("message").value="" } </script> <body> <h1>WebSocket 多人聊天室</h1> <p style="border: 1px solid black;width: 600px;height: 500px" id="talkMsg"></p> <input id="message" /> <button id="sendBtn" onclick="sendMsg()">发 送</button> </body> </html>
3.2.2、WebSocket 后台处理程序
这里我们编写了一个 sendMessage 的方法来给所有已连接的客户端进行消息的群发,而我们上面的前端代码中设置当 websocket 收到消息后,会把文本域(<p>标签)中的内容替换掉(innerHTML),因为我们的消息是不断累加到 StringBuffer 当中的,而文本域内容的替换开销并不会造成视觉上的卡顿:
/** * WebSocket 自定义处理程序 */ @Slf4j @Component public class MyWsHandler extends AbstractWebSocketHandler { // WebSocketSession 对象可以封装一下吧用户的信息封装进去 private static Map<String, SessionBean> sessionMap = new ConcurrentHashMap<>(); // 线程安全的int值 private static AtomicInteger clientIdMaker = new AtomicInteger(0); // private static StringBuffer sb = new StringBuffer(); // 连接建立 @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { super.afterConnectionEstablished(session); // 放在父方法调用之后 SessionBean sessionBean = new SessionBean(session, clientIdMaker.getAndIncrement()); sessionMap.put(session.getId(),sessionBean); log.info(sessionMap.get(session.getId()) + " connected"); sb.append(sessionBean.getClientId()).append(" 进入了群聊<br/>"); sendMessage(sessionMap); } // 收到消息 @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { super.handleTextMessage(session, message); log.info(sessionMap.get(session.getId()).getClientId() + " : " + message.getPayload()); sb.append(sessionMap.get(session.getId()).getClientId()).append(" : ").append(message.getPayload()).append("<br/>"); sendMessage(sessionMap); } // 传输异常 @Override public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { super.handleTransportError(session, exception); // 如果异常就关闭 session if (session.isOpen()) session.close(); sessionMap.remove(session.getId()); } // 连接关闭 @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { super.afterConnectionClosed(session, status); Integer clientId = sessionMap.get(session.getId()).getClientId(); log.info(sessionMap.get(session.getId()) + " closed"); sessionMap.remove(session.getId()); sb.append(clientId).append("退出了群聊<br/>"); sendMessage(sessionMap); } public void sendMessage(Map<String,SessionBean> sessionMap){ for(String sessionId: sessionMap.keySet()){ try { sessionMap.get(sessionId).getWebSocketSession().sendMessage(new TextMessage(sb.toString())); } catch (IOException e) { e.printStackTrace(); log.error(e.getMessage()); } } } }
3.2.3、测试
启动项目,并访问 http://localhost:8080/ws-client.html
可以看到,当我们启动多个客户端(开启多个网页),就会显示多人加入群聊,当任意客户端发送消息后,所有客户端都可以看到,当有客户端退出后,同样所有人都可以看到。
4、WebSocket 使用场景
总的来讲,websocket 主要的特点就是全双工通信吗,它最大的优势就是实时性特别好,因为传统的单工通信我们客户端需要不断的向服务器发送请求,要追求实时性就必须不断频繁发送请求,效率肯定是低下的。而基于 WebSocket 协议的全双工通信的话就不需要了,因为服务端下你在可以主动给客户端发送消息了,就不需要客户端不断的去发送请求了。
比如股价分析,股票什么时候发生变动我们谁也说不清楚,但是难道让客户端每几百毫秒去访问一次服务器吗?那必不可能,最好的办法就是每当股价发生变动,让服务器主动给客户端发送消息,客户端收到消息以后再做更新。这样通信效率一下子刷就上来了。
4.1、网页在线客服
4.2、chatGpt
4.3、弹幕
4.4、实时股价分析
4.5、4399小游戏
4.6、实时统计
总结
1、为什么需要 WebSocket?
HTTP 请求只能从浏览器(客户端)发送服务器,不能从服务器发往浏览器(客户端),这就导致一些需要从服务器发往浏览器(客户端)的场景满足不了。
2、WebSocket 是什么?
WebSocket 是 HTML5 支持的,它是基于现有的 HTTP 请求进行了协议升级的一个全双工通信协议。一般用于实时性要求比较高的场景。