導入maven依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
配置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 //註解開啓STOMP協議來傳輸基於代理的消息,此時控制器支持使用@MessageMapping
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic", "/user");//topic用來廣播,user用來實現p2p
//點對點使用的訂閱前綴(客戶端訂閱路徑上會體現出來),不設置的話,默認也是/user/
config.setUserDestinationPrefix("/user");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
//使用sockjs兼容pc端各大瀏覽器(微信小程序連接不了)
registry.addEndpoint("/webServer").setAllowedOrigins("*").addInterceptors().withSockJS();
//小程序連接,重點在withSockJS()
registry.addEndpoint("/wxServer").setAllowedOrigins("*").addInterceptors();
}
}
發送消息(點對點)
@Autowired
public SimpMessagingTemplate messagingTemplate;
@GetMapping("/send/test1")
public Result sendTest1() {
MsgCarrier carrier = MsgCarrier.build().withType(MessageType.ORDER_CONFIRM)
.withMsg("發送單人成功").withData(new Date());
messagingTemplate.convertAndSendToUser("123456", "/wx/push/singleton", carrier);
return Result.success("發送成功!");
}
發送消息(一對多)
@Autowired
public SimpMessagingTemplate messagingTemplate;
public Result sendTest2() {
MsgCarrier carrier = MsgCarrier.build().withType(MessageType.ORDER_CONFIRM)
.withMsg("發送多人成功").withData(new Date());
messagingTemplate.convertAndSend("/topic/wx/push/all", carrier);
return Result.success("發送成功!");
}
##在線人數計數器
import org.springframework.stereotype.Component;
/**
* 在線人數計數器(線程安全)
*
* @Author xs
* @Date 2019/4/23 10:43
*/
@Component
public class Counter {
private Long onlineCount = 0L;
/**
* 在線人數增長
*/
public synchronized void incrementCount(){
onlineCount++;
}
/**
* 在線人數減少
*/
public synchronized void decrementCount(){
onlineCount--;
}
/**
* 獲取在線人數
* @return 在線人數
*/
public Long getOnlineCount(){
if(this.onlineCount <= 0){
return 0L;
}
return this.onlineCount;
}
}
消息載體實體類
import com.yikesong.favourablelife.pojo.enums.MessageType;
import java.io.Serializable;
import java.util.Date;
/**
* websocket 消息載體
*
* @Author xs
* @Date 2019/6/15 11:01
*/
public class MsgCarrier implements Serializable {
private MessageType type;//消息類型
private String msg;//消息內容
private Object data;//消息載體數據
private Date time;//消息時間
public Date getTime() {
return time;
}
public void setTime(Date time) {
this.time = time;
}
public MessageType getType() {
return type;
}
public void setType(MessageType type) {
this.type = type;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public static MsgCarrier build() {
MsgCarrier msgCarrier = new MsgCarrier();
msgCarrier.setTime(new Date());
return msgCarrier;
}
public MsgCarrier withType(MessageType type) {
this.type = type;
return this;
}
public MsgCarrier withData(Object data) {
this.data = data;
return this;
}
public MsgCarrier withMsg(String msg) {
this.msg = msg;
return this;
}
public MsgCarrier withTime(Date time) {
this.time = time;
return this;
}
}
小程序端代碼實現
1.Socket工具類(重連機制)
/* Created by zfh on 2018/11/2 */
import { Stomp } from "./stomp";
class WebSocket {
/**
* 微信 WebSocket 任務
*/
socketTask = null;
/**
* Stomp代理
*/
stompClient = null;
/**
* 默認監聽的消息頻道
*/
channel = null;
/**
* 消息監聽器
*/
messageMonitor = null;
/**
* 消息處理器
*/
messageHandler = null;
/**
* 重連成功的回調
*/
reconnectCallback = null;
/**
* 主動斷開連接的標識
*/
disconnectFlag = false;
/**
* 默認最大重連次數
*/
RECONNECT_MAX_COUNT = 50;
/**
* 默認重連時間間隔(單位:ms)
*/
RECONNECT_TIME_INTERVAL = 1500;
/**
* 斷線重連計數
*/
RECONNECT_COUNT = 0;
constructor() {
/*setInterval是用來發心跳包的,而小程序沒有window對象*/
Stomp.setInterval = function (interval, f) {
return setInterval(f, interval);
};
Stomp.clearInterval = function (id) {
return clearInterval(id);
};
}
/**
* 建立websocket連接和頻道監聽,綁定消息處理器
* @param header 消息頭
* @param webSocketUrl 連接地址
* @param channel 監聽的頻道
* @param messageHandler 消息處理器
* @param reconnectCallback 成功回調
*/
bulidConnectAndMonitor(header, webSocketUrl, channel, messageHandler, reconnectCallback) {
var that = this;
if (!this.getSocketStatus()) {
var socketTask = wx.connectSocket({
url: webSocketUrl
});
var ws = {
send: function (frame) {
socketTask.send({ data: frame });
},
close: function (frame) {
socketTask.close(frame);
}
};
socketTask.onOpen(function (frame) {
ws.onopen(frame);
if (that.RECONNECT_COUNT > 0) {
that.reconnectCallback()
}
that.RECONNECT_COUNT = 0;
console.log("websocket連接成功");
});
socketTask.onMessage(function (frame) {
ws.onmessage(frame);
});
socketTask.onClose(function (frame) {
that.stompClient._cleanUp();
/*客戶端主動斷開連接,不啓動重連。*/
if (that.disconnectFlag) {
that.disconnectFlag = false;
console.log("websocket斷開連接");
return;
}
/*因爲是遞歸,所以使用setTimeout()來做定時器*/
setTimeout(function () {
that.RECONNECT_COUNT += 1;
console.log("重連次數:", that.RECONNECT_COUNT);
if (that.RECONNECT_COUNT >= that.RECONNECT_MAX_COUNT) {
console.log("websocket連接失敗");
return;
}
that.bulidConnectAndMonitor({}, webSocketUrl, that.channel, that.messageHandler, that.reconnectCallback);
}, that.RECONNECT_TIME_INTERVAL);
});
var stompClient = Stomp.over(ws);
that.stompClient = stompClient;
stompClient.connect(
header,
function () {
that.messageMonitor = stompClient.subscribe(channel, messageHandler);
that.socketTask = socketTask;
that.channel = channel;
that.messageHandler = messageHandler;
that.reconnectCallback = reconnectCallback;
reconnectCallback('默認通道已開啓');
console.log("默認監聽的頻道:", channel);
}
);
}
}
/**
* 設置默認消息監聽器
* @param messageHandler 消息處理器
* @param reconnectCallback 重連成功的回調
*/
setDefaultMessageMonitor(messageHandler, reconnectCallback) {
if (this.getSocketStatus()) {
this.removeDefaultMessageMonitor();
this.messageMonitor = this.stompClient.subscribe(this.channel, messageHandler);
/*更新消息處理器*/
this.messageHandler = messageHandler;
/*更新重連的回調*/
this.reconnectCallback = reconnectCallback;
console.log("默認監聽頻道:", this.channel);
}
}
/**
* 移除默認消息監聽器
*/
removeDefaultMessageMonitor() {
if (this.getSocketStatus()) {
this.messageMonitor.unsubscribe();
console.log("The default listener was removed successfully");
}
}
/**
* 自定義消息監聽器
* @param channel 監聽的頻道
* @param messageHandler 消息處理器
* @return messageMonitor 消息監聽器
*/
addMessageMonitor(channel, messageHandler) {
console.log("addMessageMonitor Socket狀態",this.getSocketStatus());
if (this.getSocketStatus()) {
console.log("新監聽頻道:", channel);
return this.stompClient.subscribe(channel, messageHandler);
}
}
/**
* 移除消息監聽器
* @param messageMonitor 消息監聽器
*/
removeMessageMonitor(messageMonitor) {
if (messageMonitor == null || JSON.stringify(messageMonitor) === '{}') {
console.log("監聽器不能爲空");
return;
}
if (this.getSocketStatus()) {
messageMonitor.unsubscribe();
console.log("The listener was removed successfully");
}
}
/**
* 發送消息
* @param channel 頻道
* @param header 消息頭
* @param body 消息體
*/
sendMessage(channel, header, body) {
if (this.getSocketStatus()) {
this.stompClient.send(channel, header, JSON.stringify(body));
}
}
/**
* 關閉連接
*/
close() {
if (this.getSocketStatus()) {
this.stompClient.disconnect();
this.disconnectFlag = true;
}
}
/**
* 獲取連接狀態
* @return boolean
*/
getSocketStatus() {
var boolean = false;
if (this.socketTask && this.socketTask.readyState) {
boolean = this.socketTask.readyState === 1;
}
console.log("websocket連接狀態:" + boolean);
return boolean;
}
}
export {
WebSocket
}
2.簡單使用
import { WebSocket} from '../utils/Socket'
Page({
data: {
},
onLoad: function () {
let socket = new WebSocket();
socket.bulidConnectAndMonitor(
{},
'ws://127.0.0.1:9101/wxServer',
'/topic/wx/push/all',
function(body){
//接收廣播頻道
console.log(body);
},
function(con){
console.log(con);
//添加一對一頻道
socket.addMessageMonitor("/user/123456/wx/push/singleton", function (body) {
console.log(body);
});
}
);
PC端連接Socket(sockjs兼容)
1.下載sockjs依賴
cnpm i sockjs --save
2.引入sockjs以及stomp
import SockJS from "sockjs-client";
import Stomp from "stompjs";
3.創建sicket連接
//scoket
initWebSocket() {
this.connection();
},
connection() {
//創建連接
var _this = this;
// 建立連接對象
let socket = new SockJS(this.webSocketUrl);
// 獲取STOMP子協議的客戶端對象
this.stompClient = Stomp.over(socket);
// 定義客戶端的認證信息,按需求配置
let headers = {
Authorization: ""
};
// 向服務器發起websocket連接
this.stompClient.connect(
headers,
frame => {
if (this.connectLock) {
console.log("鎖線程鎖已還原");
clearInterval(this.timer);
this.connectLock = false; //還原鎖
}
//點對點接收消息
this.stompClient.subscribe(
`/user/${this.userInfo.id}/spell/order`,
msg => {
}
);
//一對多訂閱消息
this.stompClient.subscribe("/topic/super/to/agent",
msg => {
});
},
err => {
// 連接發生錯誤時的處理函數
console.log("連接失敗...");
if (!this.connectLock) {
this.reconnect();
}
}
);
}, //連接 後臺
disconnect() {
if (this.stompClient) {
this.stompClient.disconnect();
}
}, // 重新連接
reconnect() {
let that = this;
if (this.connectLock) return;
this.connectLock = true;
// 斷開重連機制,嘗試發送消息,捕獲異常發生時重連
this.timer = setInterval(() => {
that.connection();
}, 5000);
},
validateToken() {
let token = getToken();
if (!token) {
console.log("未登錄,關閉連接");
if (this.timer) {
clearInterval(this.timer);
}
this.connectLock = false; //還原鎖
//斷開連接
console.log("連接已斷開");
}
},
這種方式既可以兼容PC端的Socket連接,又可以實現單個需求的socket連接(如微信小程序)