一、介绍
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。 WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。 这个webSocket实现的前提是要进行一次浏览器向服务器发送一次请求,完成第一次握手,其实就是浏览器发起一次请求,与浏览器建立连接,建立连接之后双方意见达成一致,这种一致的意见就是将要共同使用websocket协议进行之后的通信。一旦这种协议建立完成,双方就能直接互相发送消息了。
二、优点
在没有用websocket时,想要及时获取服务端的更新数据,常用的两种方法:长链接、轮询;相比较这两种方式,websocket实在是有了太大的优势,既没有轮询那种耗费网络资源,也没有长链接那种耗费服务器资源。服务器是能够自主的进行给浏览器发送消息的,这是很关键的一点,有了自主性。
三、实现
springboot整合websocket
1.maven引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2.创建配置文件,开启websocket
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.前端实现
原生js代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>websocket</title>
<script src="/js/jquery-3.0.0.js"></script>
</head>
<body>
<h3>对谁发送消息:</h3>
<span>userId:</span><input type="text" id="userId"><br>
<span>消息:</span>
<input type="text" id="message">
<button id="bt">发送消息</button>
<br>
<span>使用websocket的发送信息的方法向服务器发送消息:</span>
<input type="text" id="websocketMessage">
<button id="websocketBtn">websocket发送消息</button>
<script>
var webSocket = null;
var bt = document.getElementById('bt');
if ('WebSocket' in window) {
webSocket = new WebSocket('ws://' + location.hostname + ':' + location.port + '/webSocket/2');
} else {
alert('浏览器不支持websocket');
}
webSocket.onopen = function (event) {
console.log('建立连接');
}
webSocket.onclose = function (event) {
console.log('连接关闭');
}
webSocket.onmessage = function (event) {
alert('收到服务端发来的消息:' + event.data);
}
webSocket.onerror = function () {
alert('websocket通信发生错误');
}
webSocket.onbeforeunload = function () {
webSocket.close();
}
bt.onclick = function () {
var userId = document.getElementById('userId').value;
var message = document.getElementById('message').value;
var userIdAndMessage = {'userId': userId, 'message': message};
$.post("/sendMessage", userIdAndMessage)
}
$('#websocketBtn').click(function () {
webSocket.send($('#websocketMessage').val());
})
</script>
</body>
</html>
4.后端实现
(1)websocket连接
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
@Component
@ServerEndpoint("/webSocket/{userId}")
public class WebSocket {
private Session session;
private static CopyOnWriteArraySet<WebSocket> webSocketSet = new CopyOnWriteArraySet<>();
private static ConcurrentHashMap<Integer, Session> webSocketSession = new ConcurrentHashMap<Integer, Session>();
这样做为实现之后对指定人进行消息推送
@OnOpen
public void onOpen(Session session, @PathParam("userId") int userId) {
this.session = session;
webSocketSet.add(this);
webSocketSession.put(userId, session);
System.out.println("新连接者的用户号:" + userId);
System.out.println("当前在线用户总数:" + webSocketSet.size());
}
@OnClose
public void onClose() {
webSocketSet.remove(this);
int offLineUserId = 0;
for (int userId : webSocketSession.keySet()) {
if (webSocketSession.get(userId) == this) {
offLineUserId = userId;
webSocketSession.remove(userId);
break;
}
}
System.out.println("用户“" + offLineUserId + "”离线");
System.out.println("在线人数:" + webSocketSession.size());
System.out.println(webSocketSession);
}
@OnMessage
public void onMessage(String message) {
System.out.println("收到客户端发来的消息:" + message);
}
public static void toUserSendMessage(int userId, String message) {
try {
webSocketSession.get(userId).getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
}
public void sendBroadcastMessage(String message) {
for (WebSocket webSocket : webSocketSet) {
System.out.println("广播消息,message:" + message);
try {
webSocket.session.getBasicRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
(2)接收浏览器的请求,给指定人推送消息
import com.example.websocket.service.WebSocket;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import javax.servlet.http.HttpServletResponse;
@Controller
public class SendMessageController {
@PostMapping("/sendMessage")
public void sendMessage(int userId, String message, HttpServletResponse response) {
WebSocket.toUserSendMessage(userId, message);
}
}
四、总结说明
这里后端使用了ConcurrentHashMap来存储websocket用户连接的信息,这样就是将信息存储到了内存中,对于内存的是有一定的耗费的。如果是对于将系统部署到多机上,那么需要调整或者换其他方法(像是将websocket用户连接信息暂存到数据库中等等)来进行对用户连接信息的储存。
|