1. 概述 WebSocket 协议在2008年诞生,2011年成为国际标准。现在所有浏览器都已经支持了。WebSocket 的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话。
WebSocket可以在浏览器里使用
支持双向通信
使用很简单
WebSocket 的其他特点:
建立在 TCP 协议之上,服务器端的实现比较容易。
与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
数据格式比较轻量,性能开销小,通信高效。
可以发送文本,也可以发送二进制数据。
没有同源限制,客户端可以与任意服务器通信。
协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
2. 使用 2.1 构造函数 **WebSocket()**构造函器会返回一个 WebSocket 对象。
1 var aWebSocket = new WebSocket(url [, protocols]);
desktop
mobile
server
Chrome
Edge
Firefox
Opera
Safari
Chrome Android
Firefox for Android
Opera Android
Safari on iOS
Samsung Internet
WebView Android
WebView on iOS
Deno
Node.js
WebSocket() constructor
5Toggle history
12Toggle history
11moreToggle history
12.1Toggle history
5Toggle history
18Toggle history
14moreToggle history
12.1Toggle history
4.2Toggle history
1.0Toggle history
4.4Toggle history
4.2Toggle history
1.4Toggle history
22.0.0Toggle history
url parameter allows https, http, and relative URLs
125Toggle history
125Toggle history
124Toggle history
111Toggle history
17.3Toggle history
125Toggle history
124Toggle history
83Toggle history
17.3Toggle history
27.0Toggle history
125Toggle history
17.3Toggle history
NoToggle history
NoToggle history
3. 简单实例 下面将实现一个简单的html和spring项目在两个页面之间同步传递消息:
3.1 创建一个基础的spring项目,在pom.xml中加入如下依赖: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 //websocket依赖 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-websocket</artifactId > </dependency > //日志输出依赖 <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > </dependency > <dependency > <groupId > org.slf4j</groupId > <artifactId > jcl-over-slf4j</artifactId > </dependency >
3.2 构建一个WebSocketConfig启动项 为启动项注入bean,交给spring管理该项目:
1 2 3 4 5 6 7 8 9 10 11 12 import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.socket.server.standard.ServerEndpointExporter;@Configuration public class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter () { return new ServerEndpointExporter (); } }
3.3 WebSocketServer服务实现 WebSocketServer是用于处理WebSocket连接的服务器端组件。在实现一个WebSocketServer时,以下是一些通常必须实现的方法:
handle - 这是处理客户端连接请求的核心方法。当一个新的WebSocket连接被建立时,这个方法会被调用。
handle_open - 当一个新的WebSocket连接打开时调用,通常用于初始化连接或设置必要的变量。
handle_close - 当WebSocket连接被关闭时调用,可以在这里进行资源清理。
handle_message - 当服务器接收到来自客户端的消息时调用,用于处理接收到的数据。
handle_error - 在处理WebSocket连接时发生错误时调用,用于错误处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 import jakarta.websocket.*;import jakarta.websocket.server.PathParam;import jakarta.websocket.server.ServerEndpoint;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Component;import org.springframework.stereotype.Service;import java.io.IOException;import java.util.concurrent.CopyOnWriteArraySet;@Component @Slf4j @Service @ServerEndpoint(value="/api/websocket/{sid}") public class WebSocketServer { private static int onlineCount = 0 ; private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet <WebSocketServer>(); private Session session; private String sid = "" ; @OnOpen public void onOpen (Session session, @PathParam("sid") String sid) { this .session = session; webSocketSet.add(this ); this .sid = sid; addOnlineCount(); try { sendMessage("{\"msg\":\"conn_success\"}" ); log.info("有新窗口开始监听:" + sid + ",当前在线人数为:" + getOnlineCount()); } catch (IOException e) { log.error("websocket IO Exception" ); } } @OnClose public void onClose () { webSocketSet.remove(this ); subOnlineCount(); log.info("释放的sid为:" +sid); log.info("有一连接关闭!当前在线人数为" + getOnlineCount()); } @OnMessage public void onMessage (String message, Session session) { log.info("收到来自窗口" + sid + "的信息:" + message); for (WebSocketServer item : webSocketSet) { try { item.sendMessage(message); } catch (IOException e) { e.printStackTrace(); } } } @OnError public void onError (Session session, Throwable error) { log.error("发生错误" ); error.printStackTrace(); } public void sendMessage (String message) throws IOException { log.info("推送" + sid + "的信息:" + message); this .session.getBasicRemote().sendText(message); } public static void sendInfo (String message, @PathParam("sid") String sid) throws IOException { log.info("推送消息到窗口" + sid + ",推送内容:" + message); for (WebSocketServer item : webSocketSet) { try { if (sid == null ) { } else if (item.sid.equals(sid)) { item.sendMessage(message); } } catch (IOException e) { continue ; } } } public static synchronized int getOnlineCount () { return onlineCount; } public static synchronized void addOnlineCount () { WebSocketServer.onlineCount++; } public static synchronized void subOnlineCount () { WebSocketServer.onlineCount--; } public static CopyOnWriteArraySet<WebSocketServer> getWebSocketSet () { return webSocketSet; } }
3.4 html页面搭建 在html页面中我们主要做WebSocket对象的创建和提交数据和实时显示数据的操作,其中主要的操作为创建对象:
new WebSocket("ws://localhost:8080/api/websocket/100"):这一行代码尝试创建一个新的WebSocket连接到指定的URL,这里必须指定到**@ServerEndpoint(value=”/api/websocket/{sid}”)**这个路径之下,如果需要创建两个不同的对象,只需要将其中的id修改即可,这样可以达到动态创建不同的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <title > WebSocket Example</title > <script src ="https://code.jquery.com/jquery-3.6.0.min.js" > </script > </head > <body > <div id ="main" style ="width: 1200px;" > </div > Welcome<br /> <input id ="text" type ="text" /> <button onclick ="send()" > 发送消息</button > <hr /> <button onclick ="closeWebSocket()" > 关闭WebSocket连接</button > <button onclick ="openWebSocket()" > 开启WebSocket连接</button > <hr /> <div id ="message" > </div > <script type ="text/javascript" > var websocket = null ; function openWebSocket ( ) { if ('WebSocket' in window ) { websocket = new WebSocket ("ws://localhost:8080/api/websocket/100" ); websocket.onopen = function ( ) { setMessageInnerHTML ("WebSocket连接成功" ); }; websocket.onerror = function ( ) { setMessageInnerHTML ("WebSocket连接发生错误" ); }; websocket.onmessage = function (event ) { console .log (event.data ); try { var data = JSON .parse (event.data ); setMessageInnerHTML ("接收:" +data.msg ); } catch (e) { console .error ("解析消息失败:" , e); } }; websocket.onclose = function ( ) { setMessageInnerHTML ("WebSocket连接关闭" ); }; } else { alert ('当前浏览器不支持 WebSocket' ); } } function setMessageInnerHTML (innerHTML ) { document .getElementById ('message' ).innerHTML += innerHTML + '<br/>' ; } function closeWebSocket ( ) { if (websocket) { websocket.close (); websocket = null ; } } function send ( ) { var message = document .getElementById ('text' ).value ; if (message) { var jsonMessage = JSON .stringify ({ msg : message }); if (websocket && websocket.readyState === WebSocket .OPEN ) { websocket.send (jsonMessage); setMessageInnerHTML ("发送: " + message); document .getElementById ('text' ).value = '' ; } else { alert ("WebSocket连接未开启!" ); } } else { alert ("请输入消息!" ); } } </script > </body > </html >
4. 测试 其中是两个不同的id对应的用户
5. 扩展 5.1 项目启动 项目直接由spring启动,不需要使用到tomcat
5.2 优化 指定用户发送?
区分发送和接收?