org.springframework.boot
spring-boot-starter-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;
}
}
/* 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
}
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);
});
}
);
cnpm i sockjs --save
import SockJS from "sockjs-client";
import Stomp from "stompjs";
//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连接(如微信小程序)