簡(jiǎn)述:支持發(fā)送公告如绸、發(fā)送指定用戶(hù)及異地登錄。
spring mvc需要的jar包:
使用spring boot 可以集成websocket庫(kù),如果少了其他庫(kù)选脊,可以在啟動(dòng)spring boot項(xiàng)目看控制臺(tái)報(bào)什么錯(cuò)澎怒,再添加相對(duì)應(yīng)的庫(kù)即可褒搔。
需要的JavaScript庫(kù):
sockjs.js
stomp.js
定義收發(fā)消息實(shí)體類(lèi):
package com.test.springWebsocket;
public class WebMessage {
/**
* 用戶(hù)id
*/
private Long userId;
/**
* 用戶(hù)名
*/
private String username;
/**
* 客戶(hù)端標(biāo)記
*/
private String clientMark;
/**
* 內(nèi)容
*/
private String contents;
/**
* 消息類(lèi)型抄瑟,1.公告局雄,2.點(diǎn)對(duì)點(diǎn)發(fā)消息徙垫,3.檢查異地登錄
*/
private String type;
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getClientMark() {
return clientMark;
}
public void setClientMark(String clientMark) {
this.clientMark = clientMark;
}
public String getContents() {
return contents;
}
public void setContents(String contents) {
this.contents = contents;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
定義WebSocket配置:
package com.test.springWebsocket;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
//客戶(hù)端與服務(wù)器端建立連接的點(diǎn)衩侥,允許使用sockJs方式訪問(wèn)猴誊,允許跨域
stompEndpointRegistry.addEndpoint("/any-socket").withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry messageBrokerRegistry) {
//訂閱Broker名稱(chēng)
messageBrokerRegistry.enableSimpleBroker("/topic", "/user");
//全局使用的訂閱前綴(客戶(hù)端訂閱路徑上會(huì)體現(xiàn)出來(lái))
messageBrokerRegistry.setApplicationDestinationPrefixes("/app");
//點(diǎn)對(duì)點(diǎn)使用的訂閱前綴(客戶(hù)端訂閱路徑上會(huì)體現(xiàn)出來(lái))强霎,不設(shè)置的話撕攒,默認(rèn)也是/user/
//注意:enableSimpleBroker方法里的某個(gè)參數(shù)路徑必須和該方法的路徑要一樣扁藕,不然指定用戶(hù)發(fā)送消息將會(huì)失敗
messageBrokerRegistry.setUserDestinationPrefix("/user/");
}
}
定義WebSocketController:
package com.test.springWebsocket;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Controller;
@Controller
public class WebSocketController {
@Autowired
private SimpMessagingTemplate template;
/**
* 發(fā)送公告
*
* @param msg WebMessage
*/
@MessageMapping("/to-all")
public void toAll(WebMessage msg) {
template.convertAndSend("/topic/to-all", msg);
}
/**
* 發(fā)送指定用戶(hù)
*
* @param msg WebMessage
*/
@MessageMapping("/to-one")
public void toOne(WebMessage msg) {
Long userId = msg.getUserId();
if (userId != null) {
template.convertAndSendToUser(userId.toString(), "/to-one", msg);
}
}
}
定義jsp頁(yè)面RequestMapping:
/**
* spring WebSocket 頁(yè)面
*
* @param request HttpServletRequest
* @return String
*/
@RequestMapping("/spring-websocket.xhtm")
public String springWebsocket(HttpServletRequest request) {
String clientMark = (String) request.getSession().getAttribute("clientMark");
if (clientMark == null) {
clientMark = GenerateUtil.getUUID();
request.getSession().setAttribute("clientMark", clientMark);
}
Admin admin = (Admin) request.getSession().getAttribute("admin");
request.setAttribute("userId", admin.getId());
request.setAttribute("username", admin.getAdmin());
request.setAttribute("clientMark", clientMark);
return "springWebsocket/springWebsocket";
}
定義jsp頁(yè)面:
<!DOCTYPE html>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%
String rootPath = request.getContextPath();
String basePath = request.getScheme() + "://" +
request.getServerName() + ":" +
request.getServerPort() + rootPath + "/";
%>
<html>
<head>
<title>Spring WebSocket</title>
<meta http-equiv="Expires" content="0"/>
<meta http-equiv="Cache" content="no-cache"/>
<meta http-equiv="Pragma" content="no-cache"/>
<meta http-equiv="Cache-Control" content="no-cache"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<style type="text/css">
.msgBox {
height: 300px;
width: 800px;
border: 2px solid #2196F3;
overflow: auto;
}
</style>
<script type="text/javascript">
var rootPath = "<%=rootPath%>";
var basePath = "<%=basePath%>";
var userId = '${userId}';
var username = '${username}';
var clientMark = '${clientMark}';
</script>
<script type="text/javascript" src="<%=basePath%>static/js/plugins/jquery/jquery-1.9.1.min.js"></script>
<script type="text/javascript" src="<%=basePath%>static/js/common/sockjs.v1.0.0.js"></script>
<script type="text/javascript" src="<%=basePath%>static/js/common/stomp.v2.3.3.js"></script>
</head>
<body>
<div>用戶(hù)id:${userId}</div>
<div>用戶(hù)名:${username}</div>
<div>
<span>公告:</span><input type="text" name="notice"/>
<button type="button" id="notice">發(fā)送</button>
</div>
<div>
<span>用戶(hù)id:</span><input type="text" name="userId"/>
<span>內(nèi)容:</span><input type="text" name="contents"/>
<button type="button" id="sendToUser">發(fā)送</button>
</div>
<div>公告列表:</div>
<div id="noticeList" class="msgBox"></div>
<div>消息列表:</div>
<div id="msgList" class="msgBox"></div>
<script type="text/javascript" src="<%=basePath%>static/js/springWebsocket/springWebsocket.js"></script>
</body>
</html>
定義JavaScript腳本:
var socket = null;
var stompClient = null;
function closeSocket() {
if (socket == null || socket.readyState == 2 || socket.readyState == 3) {
return true;
}
socket.close();
}
//監(jiān)聽(tīng)窗口關(guān)閉事件,當(dāng)窗口關(guān)閉時(shí),主動(dòng)去關(guān)閉websocket連接,防止連接還沒(méi)斷開(kāi)就關(guān)閉窗口,server端會(huì)拋異常.
window.onbeforeunload = function () {
closeSocket();
};
function showMsg(webMsg) {
switch (webMsg['type']) {
//公告
case '1': {
var noticeHtm = '<div>' + webMsg['contents'] + '</div>';
$('#noticeList').append(noticeHtm);
$("#noticeList").scrollTop($("#noticeList")[0].scrollHeight);
break;
}
//點(diǎn)對(duì)點(diǎn)發(fā)消息
case '2': {
var msgHtm = '<div>' + webMsg['contents'] + '</div>';
$('#msgList').append(msgHtm);
$("#msgList").scrollTop($("#msgList")[0].scrollHeight);
break;
}
//檢查異地登錄
case '3': {
if (webMsg['clientMark'] != clientMark) {
closeSocket();
alert('您的賬號(hào)在另一處登錄');
}
break;
}
default: {
alert("WebSocket接收到未知消息...");
break;
}
}
}
function connect() {
socket = new SockJS(rootPath + '/any-socket');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
// 訂閱 /topic/to-all 實(shí)現(xiàn)公告
stompClient.subscribe(rootPath + '/topic/to-all', function (dd) {
showMsg(JSON.parse(dd.body));
});
// 訂閱 /user/userId/to-one 實(shí)現(xiàn)點(diǎn)對(duì)點(diǎn)
stompClient.subscribe(rootPath + '/user/' + userId + '/to-one', function (dd) {
showMsg(JSON.parse(dd.body));
});
var webMsg = {
'userId': userId,
'username': username,
'clientMark': clientMark,
'type': '3'
};
stompClient.send(rootPath + "/app/to-one", {}, JSON.stringify(webMsg));
});
}
connect();
$(function () {
$('#notice').on('click', function () {
if (socket == null) {
alert('WebSocket連接未打開(kāi)');
return true;
}
if (socket.readyState == 0) {
alert('WebSocket正在連接中咬像,請(qǐng)稍后再發(fā)送消息');
return true;
}
if (socket.readyState == 2) {
alert('WebSocket連接正在關(guān)閉中算撮,無(wú)法發(fā)送消息');
return true;
}
if (socket.readyState == 3) {
alert('WebSocket連接已關(guān)閉生宛,無(wú)法發(fā)送消息');
return true;
}
var webMsg = {
'contents': $('input[name="notice"]').val(),
'type': '1'
};
stompClient.send(rootPath + "/app/to-all", {}, JSON.stringify(webMsg));
});
$('#sendToUser').on('click', function () {
if (socket == null) {
alert('WebSocket連接未打開(kāi)');
return true;
}
if (socket.readyState == 0) {
alert('WebSocket正在連接中,請(qǐng)稍后再發(fā)送消息');
return true;
}
if (socket.readyState == 2) {
alert('WebSocket連接正在關(guān)閉中肮柜,無(wú)法發(fā)送消息');
return true;
}
if (socket.readyState == 3) {
alert('WebSocket連接已關(guān)閉陷舅,無(wú)法發(fā)送消息');
return true;
}
var webMsg = {
'userId': $('input[name="userId"]').val(),
'username': username,
'contents': $('input[name="contents"]').val(),
'type': '2'
};
stompClient.send(rootPath + "/app/to-one", {}, JSON.stringify(webMsg));
});
});