WebSocket是一种在单个TCP连接上进行全双工通信(双向通信)的协议,已被W3C定位标准。使用WebSocket可以使客户端与服务端的数据交换更为简单,它允许服务端主动向客户端推送数据。
在WebSocket服务中,浏览器和服务器只需要完成一次握手就可以直接创建持久性的连接,并进行双向数据传输。
常用场景:
1、在线股票网站。
2、即时聊天。
3、多人在线游戏。
4、应用集群通信。
5、系统性能实时监控。
下面转到整题,Springboot整合WebSocket:
第一个例子:没有校验用户且没有与数据库连接的网页版群聊
1、加入相关依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator-core</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>sockjs-client</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>stomp-websocket</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.3.1</version>
</dependency>
2、配置WebSocket
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
@Configuration
@EnableWebSocketMessageBroker //开启WebSocket消息代理
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config){
//设置消息代理的前缀,如果前缀为“/topic”。就会将消息转发给消息代理(broker),再由消息代理将消息广播给当前连接的客户端
config.enableSimpleBroker("/topic");
//前缀为“/app”的Destination可以通过@MessageMapping注解的方法处理,而其他前缀的交给消息代理broker处理
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry){
//定义一个前缀为“/chat”的endPoint,并开启sockJs支持
registry.addEndpoint("/chat").withSockJS();
}
}
3、定义Controller(很简单)
@Controller
public class GreetingController {
@MessageMapping("/hello")
@SendTo("/topic/greetings")
public Message greeting(Message message) throws Exception{
return message;
}
}
4、构建聊天页面(chat.html与chat.js)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>群聊</title>
<script src = "/webjars/jquery/jquery.min.js"></script>
<script src = "/webjars/sockjs-client/sockjs.min.js"></script>
<script src = "/webjars/stomp-websocket/stomp.min.js"></script>
<script src = "chat.js?t=1"></script>
</head>
<body>
<div>
<label for="name">请输入用户名:</label>
<input type="text" id="name" placeholder="用户名">
</div>
<div>
<button id="connect" type="button">连接</button>
<button id="disconnect" type="button" disabled>断开连接</button>
</div>
<div id="chat" style="display: none;">
<div>
<label for="name">请输入聊天内容</label>
<input type="text" id="content" placeholder="聊天内容">
</div>
<button id="send" type="button">发送</button>
<div id="greetings">
<div id="conversation" style="display: none">群聊进行中</div>
</div>
</div>
</body>
</html>
var stompClient = null;
//设置连接
function setConnected(connected) {
$("#connect").prop("disabled",connected);
$("#disconnect").prop("disabled",!connected);
if(connected){
$("#conversation").show();
$("#chat").show();
}else{
$("#conversation").hide();
$("#chat").hide();
}
$("#greetings").html("");
}
//创建WebSocket连接
function connect() {
if(!$("#name").val()){
return;
}
var socket = new SockJS('/chat');
stompClient = Stomp.over(socket); //创建一个STOMP实例发起请求
stompClient.connect({},function (frame) {
setConnected(true);
//订阅服务端发送回来的消息
stompClient.subscribe('/topic/greetings',function (greeting) {
showGreeting(JSON.parse(greeting.body));
});
});
}
//断开WebSocket连接
function disConnect() {
if(stompClient != null){
stompClient.disconnect();
}
setConnected(false);
}
//发送消息
function sendName() {
stompClient.send("/app/hello",{},JSON.stringify({'name':$("#name").val(),'content':$("#content").val()}));
$("#content").val("");
}
//展示消息
function showGreeting(message) {
$("#greetings").append("<div>" + message.name + ":" + message.content +"</div>");
}
$(function () {
$("#connect").click(function () {connect();});
$("#disconnect").click(function () {disConnect();});
$("#send").click(function () {sendName();});
});
5、测试启动项目(2个用户cong和kwc测试)
第二个例子:消息点对点发送
由于用户点对点聊天需要对用户进行验证,此例子用户在security的配置类中写死。
1、先加spring-security依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2、配置Security
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
PasswordEncoder passwordEncoder(){
//使用BCrypt强哈希函数,传入的10为strength的值,值越大,密钥的迭代次数越多,为2^strength
return new BCryptPasswordEncoder(10);
}
@Override
protected void configure(AuthenticationManagerBuilder auth)throws Exception{
//配置了2个用户 admin角色admin cong角色users 密码都是123
auth.inMemoryAuthentication()
.withUser("admin")
.password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq")
.roles("admin")
.and()
.withUser("cong")
.password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq")
.roles("users");
}
@Override
protected void configure(HttpSecurity http)throws Exception{
//开启HttpSecurity配置
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.permitAll();
}
3、改造第一个例子的WebSocket配置,增加queue前缀,以便区分
@Configuration
@EnableWebSocketMessageBroker //开启WebSocket消息代理
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config){
//设置消息代理的前缀,如果前缀为“/topic”或“/queue”,就会将消息转发给消息代理(broker),再由消息代理将消息广播给当前连接的客户端
config.enableSimpleBroker("/topic","/queue");
//前缀为“/app”的Destination可以通过@MessageMapping注解的方法处理,而其他前缀的交给消息代理broker处理
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry){
//定义一个前缀为“/chat”的endPoint,并开启sockJs支持
registry.addEndpoint("/chat").withSockJS();
}
}
4、在Controller中新增一个方法
@Controller
public class GreetingController {
SimpMessagingTemplate messagingTemplate;
@MessageMapping("/hello")
@SendTo("/topic/greetings")
public Message greeting(Message message) throws Exception{
return message;
}
@MessageMapping("/chat")
public void chat(Principal principal, Chat chat)throws Exception{
String from = principal.getName();
chat.setFrom(from);
messagingTemplate.convertAndSendToUser(chat.getTo(),"/queue/chat", chat);
}
}
5、创建在线聊天页面和JS(onlinechat.html和onlinechat.js)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>在线陪聊</title>
<script src = "/webjars/jquery/jquery.min.js"></script>
<script src = "/webjars/sockjs-client/sockjs.min.js"></script>
<script src = "/webjars/stomp-websocket/stomp.min.js"></script>
<script src = "onlinechat.js?t=2"></script>
</head>
<body>
<div id="chat">
<div id="chatsContent"></div>
<div>
请输入聊天内容:
<input type="text" id="content" placeholder="聊天内容">
目标用户:
<input type="text" id="to" placeholder="目标用户">
</div>
<button id="send" type="button">发送</button>
</div>
</body>
</html>
var stompClient = null;
//创建WebSocket连接
function connect() {
var socket = new SockJS('/chat');
stompClient = Stomp.over(socket); //创建一个STOMP实例发起请求
stompClient.connect({},function (frame) {
//订阅服务端发送回来的消息
stompClient.subscribe('/user/queue/chat',function (chat) {
showGreeting(JSON.parse(chat.body));
});
});
}
//发送消息
function sendMsg() {
stompClient.send("/app/chat",{},JSON.stringify({'content':$("#content").val(),'to':$("#to").val()}));
$("#content").val("");
}
//展示消息
function showGreeting(message) {
$("#chatsContent").append("<div>" + message.from + ":" + message.content +"</div>");
}
$(function () {
connect();
$("#send").click(function () {sendMsg();});
});
6、启动项目测试