SpringBoot整合WebSocket实现聊天室流程全解
欲无缘 人气:0什么是WebSocket
WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
WebSocket通信模型
为什么需要WebSocket
http 通信是单向的,发送请求获取响应,没有请求也就没有响应。
简单理解:
- HTTP 打电话:客户端问一句服务端答一句
- WebSocket 打电话:双向对话
Websocket与http的关系
相同点:
- 都是基于tcp的,都是可靠性传输协议
- 都是应用层协议
不同点:
- WebSocket是双向通信协议,模拟Socket协议,可以双向发送或接受信息
- HTTP是单向的
- WebSocket是需要浏览器和服务器握手进行建立连接的而http是浏览器发起向服务器的连接,服务器预先并不知道这个连接。
SpringBoot集成WebSocket
maven依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
WebSocketConfig
@Configuration public class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } }
WebSocketService
@Slf4j @Service @ServerEndpoint(value = "/myService/{userId}") public class WebSocketService { /** * 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。 */ private static AtomicInteger onlineCount = new AtomicInteger(0);; /** * concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。 */ private static ConcurrentHashMap<String,WebSocketService> webSocketMap = new ConcurrentHashMap<>(); /** * concurrent包的线程安全Set,用来存放每个客户端对应的session对象。 */ private static ConcurrentHashMap<String,Session> sessionMap = new ConcurrentHashMap<>(); /** * 与某个客户端的连接会话,需要通过它来给客户端发送数据 */ private Session session; private String userId = ""; /** * 连接建立成功调用的方法 */ @OnOpen public void onOpen(Session session,@PathParam("userId") String userId) { this.session = session; this.userId = userId; if(webSocketMap.containsKey(userId) && sessionMap.containsKey(userId)){ webSocketMap.remove(userId); sessionMap.remove(userId); sessionMap.put(userId,session); webSocketMap.put(userId,this); }else{ webSocketMap.put(userId,this); sessionMap.put(userId,session); addOnlineCount(); } log.info("用户连接:"+userId+",当前在线人数为:" + getOnlineCount()); } /** * 连接关闭调用的方法 */ @OnClose public void onClose() { if(webSocketMap.containsKey(userId)){ webSocketMap.remove(userId); subOnlineCount(); } log.info("用户退出:"+userId+",当前在线人数为:" + getOnlineCount()); } /** * 收到客户端消息后调用的方法 */ @OnMessage public void onMessage(String message, Session session) { this.session = session; log.info("收到客户端消息 -> {}",message); //服务端收到客户端的消息并推送给客户端 sendMessage(message); } /** * 发生错误时调用 */ @OnError public void onError(Session session, Throwable error) { log.error(error.getMessage()); } /** * 实现服务器主动推送 可以通过controller调用此方法实现主动推送 */ public void sendMessage(String message){ try { Set<Map.Entry<String, Session>> entries = sessionMap.entrySet(); for (Map.Entry<String, Session> next : entries) { Session session = next.getValue(); session.getBasicRemote().sendText(this.userId + "说" + message); } } catch (IOException e) { log.error(e.getMessage()); } } public static synchronized int getOnlineCount() { return onlineCount.get(); } public static synchronized void addOnlineCount() { WebSocketService.onlineCount.getAndIncrement(); } public static synchronized void subOnlineCount() { WebSocketService.onlineCount.getAndDecrement(); } }
WebSocket.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>websocket通讯</title> </head> <body> <p>userId:<input id="userId" name="userId" type="text" value="10"></p> <p>msg:<input id="contentText" name="contentText" type="text" value="hello websocket"></p> <p>操作:<button onclick="openSocket()">开启socket</button></p> <p>操作:<button onclick="sendMessage()">发送消息</button></p> </body> <script type="application/javascript"> let socket; function openSocket() { if(socket != null){ socket.close(); socket = null; } let userId = document.getElementById('userId').value socket = new WebSocket("ws://localhost:9000/myService/"+userId); //打开事件 socket.onopen = function() { console.log("websocket已打开"); }; //获得消息事件 socket.onmessage = function(msg) { console.log(msg.data); }; //关闭事件 socket.onclose = function() { console.log("websocket已关闭"); }; //发生了错误事件 socket.onerror = function() { console.log("websocket发生了错误"); } } function sendMessage() { let contentText = document.getElementById('contentText').value socket.send(contentText); } </script> </html>
测试
加载全部内容