Spring Boot WebSocket 笔记
本文仓库地址:chat01
一、网页技术发展过程
一开始的网页是静态的html
页面,不能进行交互,随后javascript
出现,在一定程度上实现了页面的简单交互。不过javascript
不能和服务端交互,随后ajax
的出现解决了与服务端交互的问题,ajax
存在的不足——所有的请求都是由客户端发起的,服务端进行响应,若服务端有最新的消息,则难以发送到客户端去,不过我们一直存在这种实时通讯的需求。
在没有WebSocket
之前的解决方案:
一、轮询:客户端每隔一定时间去服务端查询有没有数据
存在的问题:
会有大量的无效请求,服务端的资源被大大浪费掉了
二、长连接:去服务端查询的时候如果服务端没有数据就不响应,等有数据了再响应。
存在的问题:
①:浏览器如果在服务端响应之前有新的请求发送,就只能新建一个并发请求(或者把旧的请求断掉,重新发请求)。因为前一个请求被服务端持有,没有被释放,请求收不回来。
②:无论是tcp还是http都有连接超时说法,服务端与客户端之间连接需要定时关闭再连接。
三、Applet和Flash:都能够解决消息推送问题,不过需要浏览器支持java
和falsh
,同时这两种技术都存在安全问题(目前被废弃)。
WebSocket:
利用HTTP1.1版本的升级特性Connection: upgrade
,正常发送HTTP请求,请求头里面没有这个东西。
当浏览器发送WebSocket
请求前需要建立WebSocket
连接,首先发送一个HTTP
请求,在这个HTTP
请求头里面带上Connection: upgrade
告诉服务器要进行请求协议升级,服务端会检查是否支持客户端需要升级的协议,若支持会返回给客户端一个101
状态码,表示转换请求协议,从而实现HTTP
请求向WebSocket
请求的转换。
优势:
一、由于是用HTTP
请求建立连接的,默认端口也是80和443,默认情况下不会被防火前拦截,不需要我们进行额外配置
二、使用HTTP
协议进行握手,可以自然地集成到网络浏览器的HTTP
服务器中
三、通信数据轻量,性能开销小,同行效率高
四、支持跨域
实际应用场所:聊天、多人在线游戏、股票网站、高清视频…
二、WebSocket群聊Demo
一、新建Spring Boot项目,引入依赖:
二、利用webjars
在Maven中引入前端库(也可以使用导入js的方式)
可以Maven仓库中搜索,导入pom坐标,这些前端库将以jar包的形式出现在咱们的项目里边,实际上用webjars
本质上也是用jar包里边的js文件,只不过用导入Maven依赖的方式方便统一管理,此处附上依赖:
<!-- 前端依赖-->
<!-- https://mvnrepository.com/artifact/org.webjars/sockjs-client -->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>sockjs-client</artifactId>
<version>1.1.2</version>
</dependency>
<!-- stomp可操作协议 兼容性更好-->
<!-- https://mvnrepository.com/artifact/org.webjars/stomp-websocket -->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>stomp-websocket</artifactId>
<version>2.3.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.webjars/jquery -->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.5.1</version>
</dependency>
<!--定位器-->
<!-- https://mvnrepository.com/artifact/org.webjars/webjars-locator-core -->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator-core</artifactId>
<!-- <version>0.48</version>-->
</dependency>
三、application.yml文件中配置服务端口:
server:
port: 7723
四、创建配置类WebSocketConfig
配置WebSocket
:
@Configuration
@EnableWebSocketMessageBroker //开启WebSocket消息代理
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
//注册端点用
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
//定义了一个前缀为chat的endpoint,提供了对SockJS的支持
registry.addEndpoint("/chat").setAllowedOrigins("http://localhost:7723").withSockJS();
}
//配置消息代理
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
//消息代理前缀为topic
registry.enableSimpleBroker("/topic","/queue");
}
}
五、创建信息实体类:
@Data
public class Message {
private String name;
private String content;
}
六、创建聊天控制类
@Controller
public class GreetingController {
//发消息用
@Autowired
SimpMessagingTemplate simpMessagingTemplate;
//客户端消息往@MessageMapping地址上发
//前端页面根据@SendTo定义监听地址 (广播地址)
@MessageMapping("/hello")
@SendTo("/topic/greetings")
public Message greeting(Message message) {
System.out.println("message ="+message);
return message;
}
}
七、在static
目录下创建chat.html
页面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="/webjars/jquery/3.5.1/jquery.js"></script>
<script src="/webjars/sockjs-client/1.1.2/sockjs.min.js"></script>
<script src="/webjars/stomp-websocket/stomp.min.js"></script>
</head>
<body>
<div>
<label for="username">请输入用户名:</label>
<input type="text" id="username" placeholder="用户名">
</div>
<div>
<input type="button" value="连接" id="connect">
<input type="button" value="断开连接" id="disconnect" disabled="disabled">
</div>
<div id="chat"></div>
<div>
<label for="content">请输入聊天内容</label>
<input type="text" id="content" placeholder="聊天内容">
</div>
<input value="发送" type="button" id="send" disabled="disabled">发送</input>
<script type="text/javascript">
//全部加载完毕之后再执行js
//定义全部变量
var stompClient;
$(function () {
$("#connect").click(function () {
//在点击事件内部定义一个方法,完成websocket联接
connect();
$("#send").click(function () {
stompClient.send('/hello',{},JSON.stringify({'name':$("#username").val(),'content':$("#content").val()}))
})
$("#disconnect").click(function () {
stompClient.disconnect();
setConnect(false);
})
})
});
function connect() {
//首先判断是否输入用户名
if (!$("#username").val()) {
//若没有用户名 直接return
return;
}
//若填了用户名,就建立websocket连接 填写服务端的注册端点
var sockJS = new SockJS("/chat");
stompClient = Stomp.over(sockJS);
// 参数一 建立连接用的参数,可以不用给 参数二 连接成功之后的回调函数
stompClient.connect({}, function (frame) {
setConnect(true);
//监听服务端发回来的消息
console.log("调用之前")
stompClient.subscribe("/topic/greetings",function (greeting) {
//将json字符串转换成json对象
var msgContent = JSON.parse(greeting.body);
console.log("msgContent:"+msgContent);
$("#chat").append("<div>"+msgContent.name+':'+msgContent.content+"</div>");
})
})
}
function setConnect(connected) {
console.log("connected状态:"+connected)
//设置按钮状态
$("#connect").prop("disabled", connected);
$("#disconnect").prop("disabled", !connected);
$("#send").prop("disabled", !connected);
}
</script>
</body>
</html>
八、运行测试:
若使用Chrome
浏览器测试,可以使用添加用户的形式来模拟不同的用户: