<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-websocketartifactId>
dependency>
npm install --save reconnecting-websocket
VUE_APP_DOMAIN = '192.168.0.199:38080'
@ServerEndpoint(value ="/webSocket/{userId}",encoders = { WebSocketEncoder.class })
@Component
@Data
public class WebSocketServer {
/**
* 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
*/
private static AtomicInteger onlineNum = new AtomicInteger();
/**
* concurrent包的线程安全Set,用来存放每个客户端对应的WebSocketServer对象。
*/
private static ConcurrentHashMap<String, Session> sessionPools = new ConcurrentHashMap<>();
/**
* 发送消息
* @param msgType
* @param session
* @param message
* @throws IOException
* @throws EncodeException
*/
public static void sendMessage(String msgType,Session session, Object message) throws IOException, EncodeException {
if (session != null) {
synchronized (session) {
if (message instanceof String){
session.getBasicRemote().sendObject(Result.success(message).setType("str"));
}else{
session.getBasicRemote().sendObject(Result.success(message).setType(msgType));
}
}
}
}
/**
* 给指定用户发送信息
* @param userName
* @param message
*/
public static void sendInfo(String userName, String message) {
Session session = sessionPools.get(userName);
try {
sendMessage(null,session, message);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 建立连接成功调用
* @param session
* @param userId
*/
@OnOpen
public static void onOpen(Session session, @PathParam(value = "userId") String userId) {
try {
if (ObjectUtil.isNull(userId)){
sendMessage(null,session, null);
}else{
sessionPools.put(userId, session);
addOnlineCount();
System.out.println(RedisUtil.getUserName(userId) + "加入webSocket!当前人数为" + onlineNum);
sendMessage(null,session, "欢迎" + RedisUtil.getUserName(userId) + "加入连接!");
}
} catch (IOException | EncodeException e) {
e.printStackTrace();
}
}
/**
* 关闭连接时调用
* @param userId
*/
@OnClose
public static void onClose(@PathParam(value = "userId") String userId) {
sessionPools.remove(userId);
subOnlineCount();
System.out.println(RedisUtil.getUserName(userId) + "断开webSocket连接!当前人数为" + onlineNum);
}
/**
* 在线消息
* @param message
* @throws IOException
*/
@OnMessage
public static void onMessage(String message) throws IOException {
System.out.println(message);
sessionPools.values().forEach(v->{
try {
sendMessage(null,v, message);
} catch (Exception e) {
e.printStackTrace();
}
});
}
/**
* 群发
* @param message
* @param userIds
*/
public static void sendByUserIds(String message, List<String> userIds) {
userIds.forEach(userId->{
try {
sendMessage(null, sessionPools.get(userId), message);
} catch (IOException | EncodeException e) {
e.printStackTrace();
}
});
}
/**
* 单发
* @param msgType
* @param obj
* @param userId
*/
public static void sendByUserId(String msgType,Object obj, Long userId){
try {
sendMessage(msgType, sessionPools.get(String.valueOf(userId)), obj);
} catch (IOException | EncodeException e) {
e.printStackTrace();
}
}
/**
* 错误时调用
* @param session
* @param throwable
*/
@OnError
public static void onError(Session session, Throwable throwable) {
System.out.println("发生错误");
throwable.printStackTrace();
}
public static void addOnlineCount() {
onlineNum.incrementAndGet();
}
public static void subOnlineCount() {
onlineNum.decrementAndGet();
}
}
@Configuration
public class WebSocketConfig {
@Bean(name="serverEndpointExporter")
public ServerEndpointExporter getServerEndpointExporterBean(){
return new ServerEndpointExporter();
}
}
public class WebSocketEncoder implements Encoder.Text<Result> {
@Override
public String encode(Result object) throws EncodeException {
if (object.getData() instanceof String){
return JSONUtil.toJsonStr(object);
}else{
BeanWrapper bw = new BeanWrapperImpl(object.getData());
PropertyDescriptor[] pd = bw.getPropertyDescriptors();
JSONObject jsonObject = new JSONObject();
Arrays.stream(pd).filter(f-> ObjectUtil.isNotEmpty(bw.getPropertyValue(f.getName()))).forEach(v->{
jsonObject.putIfAbsent(v.getName(),bw.getPropertyValue(v.getName())+"");
});
JSONObject parseObj = JSONUtil.parseObj(object.getData());
parseObj.putAll(jsonObject);
//将java对象转换为json字符串
return JSONUtil.toJsonStr(object.setData(parseObj));
}
}
@Override
public void init(EndpointConfig endpointConfig) {
}
@Override
public void destroy() {
}
}
@Data
@Accessors(chain = true)
public class Result {
/**
* 返回状态码 0 正常,-1 错误
*/
Integer code;
/**
* 返回信息
*/
String msg;
/**
* 返回数据
*/
Object data;
/**
* 类型
*/
String type;
/**
* 请求成功返回
* @author 骆超
* @date 2021/5/17
* @param data 参数说明
* @return com.test.activitidemo.config.Result
*/
public static Result success(Object data){
return new Result().setCode(0).setMsg("请求成功").setData(data);
}
/**
* 请求失败返回
* @author 骆超
* @date 2021/5/17
* @param msg 参数说明
* @return com.test.activitidemo.config.Result
*/
public static Result failed(String msg){
return new Result().setCode(-1).setMsg(msg);
}
/**
* 自定义设置返回信息
* @author 骆超
* @date 2021/5/17
* @return com.test.activitidemo.config.Result
*/
public static Result get(){
return new Result();
}
}
import ReconnectingWebSocket from 'reconnecting-websocket';
import store from '@/store'
import {Notice} from 'iview'
import {seenMessage,getMessageData} from "@/api/data";
import router from "@/router";
export default {
// 创建 websocket 链接
createWebsocket(userId) {
const httpURL = process.env.VUE_APP_DOMAIN;
this.websocket = new ReconnectingWebSocket(`ws://` + httpURL + `/webSocket/` + userId);
// 连接发生错误的回调方法
this.websocket.onerror = this.websocketOnerror;
// 连接成功建立的回调方法
this.websocket.onopen = this.websocketOnopen;
// 接收到消息的回调方法
this.websocket.onmessage = this.websocketOnmessage;
// 连接关闭的回调方法
this.websocket.onclose = this.websocketOnclose;
// 监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
this.websocket.onbeforeunload = this.websocketOnbeforeunload;
},
// 连接发生错误的回调方法
websocketOnerror() {
console.log('连接发生错误的回调方法');
},
// 连接成功建立的回调方法
websocketOnopen() {
},
// 接收到消息的回调方法
websocketOnmessage(event) {
let obj = JSON.parse(event.data)
switch (obj.type) {
case "msg":
store.commit('setMsgData', obj.data)
Notice.config({
duration: 10//消息提示停留10秒
})
Notice.info({
title: obj.data.title,
name:obj.data.id,
desc: obj.data.content,
render: h => {
return h('span', {
style: {
cursor:"pointer"
},
on: {
click: () => {
switch (obj.data.msgType) {
case 1:
case "1":
router.push({
name: 'activiti-tasks'
})
break;
default:
console.log(obj.data)
break
}
seenMessage({id:obj.data.id}).then(res=>{
store.commit('setMsgData',[])
getMessageData({}).then(res => {
if (res.code === '0') {
store.commit('setMsgData', res.data)
Notice.close(obj.data.id)
}
})
})
}
}
},obj.data.content)
}
});
break;
default:
console.log(obj.data)
}
},
// 连接关闭的回调方法
websocketOnclose(event) {
console.log("连接已关闭")
},
// 监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常
websocketOnbeforeunload() {
this.closeWebSocket();
console.log('监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常');
},
// 关闭WebSocket连接
closeWebSocket() {
this.websocket.close();
},
}
前端在初次登录的时候,调用createWebsocket传入用户id,与后端建立连接,后端在调用的时候调用WebSocketServer 中的sendByUserId方法即可