springboot整合WebSocket,实现聊天室功能!
一、增加WebSocket依赖
<!-- springboot整合WebSocket进行全双工通信(1) -->
<!-- websocket相关依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!-- 前端库,使用jar包的形式对前端库进行统一管理,使用webjar添加到项目中的前端库,在springboot项目中已经默认添加了静态资源过滤,因此可以直接使用 -->
<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>
二、定义配置类,实现WebSocketMessageBrokerConfigurer接口,设定消息代理的前缀及支持sockjs的代码
package com.steno.propertiestest.common;
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;
/**
* springboot整合WebSocket进行全双工通信(2)
* 自定义类实现WebSocketMessageBrokerConfigurer接口,实现特定方法
*/
@Configuration //定义配置类
@EnableWebSocketMessageBroker //开启WebSocket消息代理
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
/**
* @param registry 消息代理注册
*/
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
/*
* 设置消息代理的前缀,即:如果消息的前缀是"/topic"就会将消息转发给消息代理(broker),
* 再由消息代理降消息广播给当前连接的客户端
*/
registry.enableSimpleBroker("/topic");
/*
* 配置一个或多个前缀,通过这些前缀过滤出需要被注解方法处理的消息!
* 如:前缀为"/app"的destionation(目的地)可以通过@MessageMapping注解的方法处理,
* 而其他destionnation(如"/topic"、"/queue")将被直接交给broker(消息代理)处理。
*/
registry.setApplicationDestinationPrefixes("/app");
}
/**
* @param registry
*/
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
/*
* 定义一个前缀为"/chat"的endPoint(终点),并开启sockjs支持,sockjs可以解决浏览器对WebSocket的兼容新问题,
* 客户端将通过这里配置的url来建立WebSocket连接。
*/
registry.addEndpoint("/chat").withSockJS();
}
}
三、定义Controller实现页面跳转和消息接收后处理的功能
package com.steno.propertiestest.controller;
import com.steno.propertiestest.vo.WebSocketMessage;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
import java.util.Date;
import java.util.UUID;
/**
* springboot整合WebSocket进行全双工通信(4)
* 处理接收到的消息类
*/
@Controller
public class WebSocketHandReceivedMesController {
/**
* 由于项目集成了Shiro安全验证,因此登录后做跳转
* @return
*/
@GetMapping("/getWebSocketTestPage")
public ModelAndView getWebSocketTestPage(){
return new ModelAndView("chat");
}
/**
* 接收"/app/handleMessage"路径的请求发送来的消息,并对消息进行手动处理后,
* 再把消息发送到SendTo定义的路径上,而SendTo路径是一个"/topic"的路径,因此
* 该消息将被交给broker(消息代理),再由borker进行广播。
* @param message
* @return
*/
@MessageMapping("/handleMessage")
@SendTo("/topic/handleMessages")
public WebSocketMessage handleMessage(WebSocketMessage message){
//服务器端对接收到的消息的额外处理
message.setMessageid(UUID.randomUUID().toString());
message.setCreatedate(new Date());
return message;
}
}
四、新建消息聊天内容输入页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>springboot整合WebSocket进行全双工通信(5)---群聊功能页面展示</title>
<!-- 引入外部的JS库,该JS库是从pom.xml文件中通过依赖加入捡来的 -->
<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>
<!-- 因此自定义的JS,实现Stomp.js的socket连接设定 -->
<script src="/js/chat.js"></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="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="dispaly:none">群聊进行中....</div>
</div>
</div>
</body>
</html>
五、前端JS使用Stomp进行websocket连接和消息内容获取
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;
}
//使用SockJS建立连接
var socket = new SockJS('/chat');
//创建一个Stomp实例发起连接请求,在连接成功的回调方法中,执行特定方法处理
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame){
//页面设置
setConnected(true);
//调用subscribe方法订阅服务端发送回来的消息,并将服务端发送来的消息展示出来
stompClient.subscribe('/topic/handleMessages', function(result){
//展示消息
showGreeting(JSON.parse(result.body));
});
});
}
/**
* 发送用户名和消息内容给服务器端处理
*/
function sendName(){
stompClient.send("/app/handleMessage", {}, JSON.stringify({'name' : $("#name").val(), 'content' : $("#content").val()}));
}
function showGreeting(message){
$("#greetings").append(""
+"消息名称:"
+message.name+"
"
+" 消息内容:"+message.content+"
"
+" 消息创建时间:"+message.createdate+"
"
+" 消息的id:"+message.messageid+""
+"--------------------华丽的分割线---------------------------");
}
/**
* 断开一个WebSocket连接
*/
function disconnect(){
if(stompClient != null){
stompClient.disconnect();
}
setConnected(false);
}
$(document).ready(function(){
$("#connect").click(function(){ connect() });
$("#disconnect").click(function(){ disconnect(); });
$("#send").click(function(){ sendName(); });
});
六、浏览器访问测试
(1)Chrome浏览器访问:
http://localhost:8081/getWebSocketTestPage
(2)其他浏览器访问:
注意:
访问页面时注意websocket连接,其中ws对应HTTP,wss对应HTTPS,请求头中有2个特殊字段,Connection: upgrade表示客户端想要对协议进行升级;Upgrade: websocket表示客户端想要将请求协议升为websocket协议,这两个字段共同告诉服务器要将连接升级为websocket这样一种全双工协议,如果服务器端同意协议升级,那么在握手完成之后,文本消息和其他二进制消息就可以同时在两个方向上进行发送,而不需要关闭和重建连接。此时客户端和服务端是对等的,可以互相向对方主动发送消息。
(3)不同浏览器输入不同的用户名,用于区分不同人发送的消息
可以看到:在左侧的浏览器点击发送消息后,右侧的浏览器也接收到消息并显示内容了!
在右侧的浏览器点击发送消息后,左侧的浏览器也接收到消息并显示内容了!
七、浏览器控制台详细输出:
Opening Web Socket...
stomp.min.js:8 Web Socket Opened...
stomp.min.js:8 >>> CONNECT
accept-version:1.1,1.0
heart-beat:10000,1000
------------------------------------------------------------------------
<<< CONNECTED
version:1.1
heart-beat:0,0
user-name:admin
------------------------------------------------------------------------
>>> SUBSCRIBE
id:sub-0
destination:/topic/handleMessages
------------------------------------------------------------------------
>>> SEND
destination:/app/handleMessage
content-length:54
{"name":"小哥哥","content":"小姐姐你好啊!"}
------------------------------------------------------------------------
<<< MESSAGE
destination:/topic/handleMessages
content-type:application/json;charset=UTF-8
subscription:sub-0
message-id:qls3311i-0
content-length:149
{"messageid":"6e8e8b2c-28a3-4b89-a8fc-0c36a06a5e8b","name":"小哥哥","content":"小姐姐你好啊!","createdate":"2020-05-27T09:32:28.589+0000"}
------------------------------------------------------------------------
<<< MESSAGE
destination:/topic/handleMessages
content-type:application/json;charset=UTF-8
subscription:sub-0
message-id:qls3311i-2
content-length:146
{"messageid":"f496dd16-a8f6-4388-be4c-0f3c57a8d469","name":"小姐姐","content":"小哥哥好啊!","createdate":"2020-05-27T09:34:04.164+0000"}