1.WebSocket 简介
WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
WebSocket特点:
1.建立在 TCP 协议之上,服务器端的实现比较容易。
2.与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此3.握手时不容易屏蔽,能通过各种HTTP 代理服务器。
4.数据格式比较轻量,性能开销小,通信高效。
5.可以发送文本,也可以发送二进制数据。
6.没有同源限制,客户端可以与任意服务器通信。
7.协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
HTTP缺点:通过反复的轮训去查看资源是否有更新,对网络和资源有很大的消耗
websocket可以反向通知,双向通讯
2.WebSocket 实战
导入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
配置类
@Configuration public class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter(){ return new ServerEndpointExporter(); } }
WebSocketServer类
@Component @ServerEndpoint("/websocket/{userId}") public class WebSocketServer { /** * 日志工具 */ protected static final Logger logger= LoggerFactory.getLogger(WebSocketServer.class); /** * 与某个客户端的连接会话,需要通过它来给客户端发送数据 */ private Session session; /** * 用户id */ private String userId; /** * 用来存放每个客户端对应的MyWebSocket对象 */ private static CopyOnWriteArraySet<WebSocketServer> webSockets = new CopyOnWriteArraySet<>(); /** * 用来存在线连接用户信息 */ private static ConcurrentHashMap<String, Session> sessionPool = new ConcurrentHashMap<String, Session>(); /** * 链接成功调用的方法 */ @OnOpen public void onOpen(Session session, @PathParam(value = "userId") String userId) { try { this.session = session; this.userId = userId; webSockets.add(this); sessionPool.put(userId, session); logger.info("有新的客户连接,总数为:" + webSockets.size()); } catch (Exception e) { } } /** * 链接关闭调用的方法 */ @OnClose public void onClose() { try { webSockets.remove(this); sessionPool.remove(this.userId); logger.info("【websocket消息】连接断开,总数为:" + webSockets.size()); } catch (Exception e) { } } /** * 收到客户端消息后调用的方法 */ @OnMessage public void onMessage(String message) { logger.info("【websocket消息】收到客户端消息:" + message); } /** * 发送错误时的处理 * * @param session * @param error */ @OnError public void onError(Session session, Throwable error) { logger.info("用户错误,原因:" + error.getMessage()); error.printStackTrace(); } /** * 此为广播消息 */ public void sendAllMessage(String message) { logger.info("【websocket消息】广播消息:" + message); for (WebSocketServer webSocket : webSockets) { try { if (webSocket.session.isOpen()) { webSocket.session.getAsyncRemote().sendText(message); } } catch (Exception e) { e.printStackTrace(); } } } /** * 此为单点消息 */ public void sendOneMessage(String userId, String message) { Session session = sessionPool.get(userId); if (session != null && session.isOpen()) { try { logger.info("【websocket消息】 单点消息:" + message); session.getAsyncRemote().sendText(message); } catch (Exception e) { e.printStackTrace(); } } } /** * 此为单点消息(多人) */ public void sendMoreMessage(String[] userIds, String message) { for (String userId : userIds) { Session session = sessionPool.get(userId); if (session != null && session.isOpen()) { try { logger.info("【websocket消息】 单点消息:" + message); session.getAsyncRemote().sendText(message); } catch (Exception e) { e.printStackTrace(); } } } } }
服务端发送消息实例
@GetMapping("/hello") public void hello(){ JSONObject jsonObject=new JSONObject(); jsonObject.put("key","hello"); webSocketServer.sendAllMessage(JSONObject.toJSONString(jsonObject)); }
客户端
<html lang="en"> <head> <meta charset="UTF-8" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Vite + Vue</title> </head> <body> <p>【socket开启者的ID信息】:<div><input id="userId" name="userId" type="text" value="10"></div> <div><input id="contentText" name="contentText" type="text" value="hello websocket"></div> <p>【操作】:<button><a onclick="openSocket()">发送消息</a></button> </body> <script> let socket; function openSocket() { const socketUrl = "ws://localhost:8080/websocket/"+$("#userId").val(); socket = new WebSocket(socketUrl); //打开事件 socket.onopen = function() { console.log("websocket已打开"); socket.send('{"toUserId":"'+$("#userId").val()+'","contentText":"'+$("#contentText").val()+'"}'); console.log('{"toUserId":"'+$("#userId").val()+'","contentText":"'+$("#contentText").val()+'"}'); }; //获得消息事件 socket.onmessage = function(msg) { console.log(msg.data); //发现消息进入,开始处理前端触发逻辑 }; //关闭事件 socket.onclose = function() { console.log("websocket已关闭"); }; //发生了错误事件 socket.onerror = function() { console.log("websocket发生了错误"); } } </script> </html>