后端代码
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
/**
* @author
*
* WebSocket配置类
*/
@Slf4j
@Configuration
@AllArgsConstructor
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
private RemoteTokenServices tokenService;
public static final String USER_DESTINATION_PREFIX = "/other/";
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
log.info("WebSocket服务器注册");
//配置两个入口,原因是因为小程序不支持sockjs
registry.addEndpoint("/ws")
.setAllowedOrigins("*")
.withSockJS();
registry.addEndpoint("/wsapp")
.setAllowedOrigins("*");
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
log.info("WebSocket服务器启动");
//心跳检测
ThreadPoolTaskScheduler tpts = new ThreadPoolTaskScheduler();
tpts.setPoolSize(1);
tpts.setThreadNamePrefix("wss-heartbeat-thread-");
tpts.initialize();
///信息接收头
registry.enableSimpleBroker("/topic", "/user").setHeartbeatValue(new long[]{10000,10000}).setTaskScheduler(tpts); ;
//接收前缀
registry.setApplicationDestinationPrefixes("/app");
//请求前缀
registry.setUserDestinationPrefix(USER_DESTINATION_PREFIX);
}
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(new ChannelInterceptor() {
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
// 判断是否首次连接请求
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
String tokens = accessor.getFirstNativeHeader("Authorization");
log.info("webSocket token is {}", tokens);
if (StrUtil.isBlank(tokens)) {
return null;
}
// 验证令牌信息
OAuth2Authentication auth2Authentication = tokenService.loadAuthentication(tokens.split(" ")[1]);
if (ObjectUtil.isNotNull(auth2Authentication)) {
SecurityContextHolder.getContext().setAuthentication(auth2Authentication);
accessor.setUser(() -> auth2Authentication.getName());
return message;
} else {
return null;
}
}
//不是首次连接,已经成功登陆
return message;
}
});
}
}
@SubscribeMapping("/topic/pc")
public R subPc() {
return R.ok("订阅广播成功,当前时间:"+new Date());
}
前端代码
vue连接
先npm sockjs-client和stompjs
之后在页面引用
import SockJS from ‘sockjs-client’;
import Stomp from ‘stompjs’;
connection() {
let headers = {
Authorization: 'Bearer ' + "4cf7d2df-f4a2-4295-b267-03dca1910459"
};
// 建立连接对象,这里配置了代理
this.socket = new SockJS('/other/ws', null, {
timeout: 10000
}); //连接服务端提供的通信接口,连接以后才可以订阅广播消息和个人消息
// 获取STOMP子协议的客户端对象
this.stompClient = Stomp.over(this.socket);
// 向服务器发起websocket连接
this.stompClient.connect(
headers,
() => {
this.stompClient.subscribe('/app/topic/pc', function(greeting) {
console.log(greeting, 668);
});
},
err => {
console.log("订阅失败")
}
);
之后使用uni-app生成h5和微信小程序
先npm sockjs-client和stompjs
之后去node——mode_modules把sockjs和stompjs都都复制出来
import webScoket from '../../static/js/socket'
let stompClient = ''
import {
Stomp
} from '../../static/js/stomp.js';
import
SockJS
from '../../static/js/sockjs.min.js';
生命周期,利用uni-app的选择编码对微信小程序和h5做区分
onShow() {
// #ifdef MP-WEIXIN
const that = this
var headers = {
Authorization: 'Bearer ' + "4cf7d2df-f4a2-4295-b267-03dca1910459"
};
Promise.all([webScoket.init(), webScoket.client()]).then(result => {
stompClient = result[1]
stompClient.connect(headers, () => {
stompClient.subscribe('/app/topic/pc', response => {
that.oj=response;
console.log('收到订阅消息')
if (response.body) {
const res = JSON.parse(response.body)
// 业务逻辑
}
})
})
})
// #endif
},
onHide() {
// #ifdef MP-WEIXIN
app.globalData.isReConnect = false
stompClient.disconnect()
// #endif
},
onUnload() {
// #ifdef MP-WEIXIN
app.globalData.isReConnect = false
stompClient.disconnect()
// #endif
},
onLoad() {
// #ifdef H5
var info = uni.getSystemInfoSync();
console.log('platform:: ' + info.platform);
if (info.platform == 'other' && typeof window === 'object') {
window.WebSocket = uni.webSocketBackup;
}
this.say();
// #endif
},
say() {
var that = this;
var headers = {
Authorization: 'Bearer ' + "4cf7d2df-f4a2-4295-b267-03dca1910459"
};
var socket = new SockJS('http://10.10.10.110/other/ws');
var stompClient = Stomp.over(socket);
stompClient.connect(
headers,
() => {
stompClient.subscribe('/app/topic/pc', function(greeting) {
console.log(greeting, 668);
});
},
err => {
console.log("订阅失败")
}
);
},
socket.js
const appData = getApp()
const webScoket = {
// 创建基于STOMP协议的WebSocket
client: function () {
const Stomp = require('./stomp.min.js').Stomp
// setInterval是用来发心跳包的,而小程序没有window对象,所以要重新定义
Stomp.setInterval = (interval, f) => {
return setInterval(f, interval)
}
Stomp.clearInterval = (interval, f) => {
return clearInterval(id)
}
return new Promise((resolve, reject) => {
try {
const stompClient = Stomp.over(appData.globalData.ws)
resolve(stompClient)
} catch (e) {
reject(e)
}
})
},
// 初始化
init: function (url = '') {
// 增加全局webscoket配置
appData.globalData.isConnected = false
appData.globalData.isReConnect = true // 允许断线重连
appData.globalData.reConnectLimit = -1 // 断线重连次数,-1不限次数
appData.globalData.msgQueue = []
appData.globalData.wsUrl = url || 'ws://10.10.10.110/other/wsapp'//这里对应ws地址(不带.withSockJS();的入口)
appData.globalData.ws = {
send: this.sendMsg,
close: this.disConnect,
onopen: null,
onclose: null,
onmessage: null
}
const that = this
return new Promise((resolve, reject) => {
//连接
this.connect({
success(msg) {
that.onOpen() // 打开连接
that.onMsg() //接收数据
that.onError() //监听连接错误
that.onClose() // 监听连接是否关闭
resolve(msg)
},
fail(err) {
reject(err)
}
})
})
},
// 创建一个WebSocket连接,params:{url:'String',success:'successCallback',fail:'failCallback'}
connect: function (params = {}) {
console.log('connect')
wx.connectSocket({
url: appData.globalData.wsUrl,
header:{
Authorization: 'Bearer ' + "4cf7d2df-f4a2-4295-b267-03dca1910459"
},
success: (msg) => {
if (params.hasOwnProperty('success')) {
params.success(msg)
}
},
fail: (err) => {
if (params.hasOwnProperty('fail')) {
params.fail(err)
}
}
})
},
// 监听WebSocket连接打开事件
onOpen: function () {
console.log('onopen')
wx.onSocketOpen((res) => {
console.log('WebSocket连接已打开')
appData.globalData.isConnected = true
// 执行队列里未发送的任务
appData.globalData.msgQueue.forEach(item => {
this.sendMsg(item)
})
appData.globalData.msgQueue = []
appData.globalData.ws.onopen && appData.globalData.ws.onopen()
})
},
// 发送消息
sendMsg: function (msg) {
console.log('sendmsg')
// 如果WebSocket已连接则发送消息
if (appData.globalData.isConnected) {
wx.sendSocketMessage({
data: msg
})
} else {
// WebSocket没有连接将消息放入队列中
appData.globalData.msgQueue.push(msg)
}
},
// 监听WebSocket接受到服务器的消息事件
onMsg: function () {
console.log('onmsg')
wx.onSocketMessage((res) => {
console.log('WebSocket收到消息事件:', res)
appData.globalData.ws.onmessage && appData.globalData.ws.onmessage(res)
})
},
// 监听WebSocket连接错误事件
onError: (res) => {
console.log('onerror')
wx.onSocketError((res) => {
console.log("WebSocket错误事件:", res)
})
},
// 关闭WebSocket连接
disConnect: function () {
console.log('disconnect')
wx.closeSocket()
},
// 监听WebSocket连接关闭事件
onClose: function () {
console.log('onclose')
wx.onSocketClose((res) => {
console.log('WebSocket连接关闭:', res)
appData.globalData.ws.onclose && appData.globalData.ws.onclose(res)
appData.globalData.isConnected = false
// 断线重连
if (appData.globalData.isReConnect) {
// 调整重连的次数
if (!appData.globalData.hasOwnProperty('reConnectLimit') || appData.globalData.reConnectLimit === 0) {
appData.globalData.isReConnect = false
} else {
if (appData.globalData.reConnectLimit > 0) {
appData.globalData.reConnectLimit--
}
console.log('剩余重连次数:', appData.globalData.reConnectLimit)
this.connect({
fail(err) {
console.log('重新连接失败:', err)
}
})
}
}
})
}
}
export default webScoket
之后可以通过发行微信将uni-app转换成微信小程序代码