Websocket vue+Springboot前后端案例

前端js

根目录创建一个js文件
在这里插入图片描述

import Vue from 'vue'
//创建websocket对象

import store from '@/store/index'

let webSocket = null;
//创建webSocket连接

let createWebSocket = () => {
    try {
        let url = window.location.href
        const ip = url.split('//')[1].split('/')[0]
        let wsUri = "ws://scmallfuwuqi.cn.utools.club/ucar/websocket/"+store.state.user.username //ws://localhost:8080/contenxt-path/url/..
        //其实只需要定义一个变量存放后端提供的WebSocket接口地址,然后
        console.log(wsUri, 'wsUri')
        webSocket = new WebSocket(wsUri);
        console.log(webSocket, 'webSocket')
        //初始化websocket连接
        initWebsocket();
    } catch (e) {
        console.log('尝试创建连接失败');
        //如果无法连接上webSocket 那么重新连接!可能会因为服务器重新部署,或者短暂断网等导致无法创建连接
        reConnect();
    }
};
//连接标识 避免重复连接
let isConnect = false;
//断线重连后,延迟5秒重新创建WebSocket连接  rec用来存储延迟请求的代码
let rec;
//定义重连函数
let reConnect = () => {
    console.log('尝试重新连接');
    //如果已经连上就不在重连了
    if (isConnect) return;
    rec && clearTimeout(rec);
    // 延迟5秒重连  避免过多次过频繁请求重连
    rec = setTimeout(function () {
        createWebSocket();
    }, 5000);

};
//初始化webSocket连接
let initWebsocket = () => {

    //WebSocket连接建立之后会调用onopen方法
    webSocket.onopen = function (e) {
        console.log('初始化,open');
        //连接建立后修改标识
        isConnect = true;
        // 建立连接后开始心跳
        // 因为nginx一般会设置例如60s没有传输数据就断开连接  所以要定时发送数据
        heartCheck.start();
    };
    //当websocket收到服务器发送的信息之后  会调用onmessage方法  getMsg用来封装获取到服务器的消息进行处理,下面会说明
    webSocket.onmessage = function (e) {
        getMsg(e);
        console.log(e)
        //获取消息后 重置心跳
        heartCheck.reset();
    };
    //当websocket因为各种原因(正常或者异常)关闭之后,会调用onclose方法
    webSocket.onclose = function (e) {
        console.log('close', '关闭');
        //连接断开后修改标识
        isConnect = false;
       // reConnect();
    };
    //当websocket因为异常原因(比如服务器部署、断网等)关闭之后,会调用onerror方法
    //在onerror中需要调用reConnect方法重连服务器
    webSocket.onerror = function (e) {
        console.log('close', '出错了');
        console.log('error');
        //连接断开后修改标识
        isConnect = false;
        //连接错误 需要重连
        reConnect();
    };
};

//心跳发送/返回的信息
//服务器和客户端收到的信息内容如果如下 就识别为心跳信息 不要做业务处理
let checkMsg = 'heartbeat';

//心跳设置
var heartCheck = {
    //每段时间发送一次心跳包 这里设置为20s
    timeout: 200000,
    //延时发送消息对象(启动心跳新建这个对象,收到消息后重置对象)
    timeoutObj: null,
    //一段时间后发送心跳包
    start: function () {
        this.timeoutObj = setTimeout(function () {
            console.log('心跳1', Date.now())
            if (isConnect) webSocket.send(checkMsg);
            console.log('心跳2', Date.now())
        }, this.timeout);
    },
    //    接收到服务器的信息之后要重置心跳发送的方法
    reset: function () {
        clearTimeout(this.timeoutObj);
        this.start();
    },
};
//获得消息之后   区别是心跳还是业务信息  如果是业务信息特殊处理(这里就用Element的notify才处理提醒)
let getMsg = (e) => {
    console.log('message');
    console.log(e.data);
    if(JSON.parse(e.data)!=='保持心跳'){
        store.commit('updateMessage', JSON.parse(e.data))
    }
};

//关闭连接
let closeWebSocket = () => {
    webSocket.close();
};


export default {
    createWebSocket: createWebSocket,
    closeWebSocket: closeWebSocket,
}

如果果断来了消息会放在Vuex中
在这里插入图片描述
然后有个难题,我们怎么去监听state中的message这个对象呢?
在我们的组件中添加计算属性,然后给这个计算属性配上侦听器

 computed: {
    getUserIcons() {
      return this.$store.state.message;//在Vuex中拿值
    },
   },
   watch: {
    getUserIcons(val) {
      this.messages.push(val);//messages是该组件在Data中一个数组绑定了组件上一个数组
      console.log(val, "触发了监听器");
      console.log(this.messages);
    },
    deep: true,
  },

ok在组件中初始化

在组件中调用

<template>
	{{messages}}
</template>

<script>
import websocket from "../../websocket/websocket";
export default {
	data(){
		return{
			messages:[]
		}
	},
	methods:{
	 initWebSocket() {
      if (!this.$store.state.user) return;
      websocket.createWebSocket();
    },
	},
	created(){
		this.initWebSocket();
	},
	 computed: {
    getUserIcons() {
      return this.$store.state.message;//在Vuex中拿值
    },
   },
   watch: {
    getUserIcons(val) {
      this.messages.push(val);//messages是该组件在Data中一个数组绑定了组件上一个数组
      console.log(val, "触发了监听器");
      console.log(this.messages);
    },
    deep: true,
  },
}
</script>

<style scoped>
</style>

前端就完成了

Springboot

首先确保你的跨域过滤器对上面ws://xxxxxx/xx是不做拦截的!放过这个请求地址
首先在你项目的commons中创建一个类叫WebSocketStock,用于存放目前连接着的websocketClient


import com.etoak.ucar.service.impl.WebSocket;
import lombok.Data;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.HashMap;



@Data
public class WebSocketStock {
   private static HashMap<String, WebSocket> stock =new HashMap<>();

   public static void addToStock(String name,WebSocket webSocket){
       stock.put(name,webSocket);
   }

   public static HashMap<String,WebSocket> getStock(){
       return stock;
   }
}

在你的service/impl中创建以下WebSocket核心类

import com.etoak.commons.Stock.WebSocketStock;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import java.util.Set;


@Component
@ServerEndpoint("/websocket/{userName}")
@Slf4j
@Data
public class WebSocket {



    private Session session;

    public WebSocket(){

    }
    @OnOpen
    public void onOpen(@PathParam("userName")String userName,Session session){
        this.session =session;
        WebSocketStock.addToStock(userName,this);
        System.out.println(WebSocketStock.getStock().toString());
        log.info("【websocket消息】有新的连接,总数:{}",WebSocketStock.getStock().size());
    }
    @OnClose
    public void onClose(){
//        Collection c =stock.getStock().values();
//        c.remove(this);
        Collection<WebSocket> values = WebSocketStock.getStock().values();
        try{ values.remove(this);}catch (Exception e){e.printStackTrace();}
        log.info("【websocket消息】连接断开,总数:{}",WebSocketStock.getStock().size());
    }
    @OnMessage
    public void onMessage(String message){
        log.info("【websocket消息】收到客户端发来的消息:{}",message);
    }
    public void sendMessage(String userName,String message){
        Set<Map.Entry<String, WebSocket>> entries = WebSocketStock.getStock().entrySet();
        for (Map.Entry<String, WebSocket> entry : entries) {
            if(entry.getKey().equals(userName)){
                try {
                    log.info("【websocket消息】发送消息:{}",message);
                    entry.getValue().getSession().getBasicRemote().sendText(message);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

另外在WebSocket中如果超过一分钟没有通信会自动断开,所以在你的config中建立SaticScheduleTask

import com.etoak.commons.Stock.WebSocketStock;
import com.etoak.ucar.service.impl.WebSocket;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;

@Configuration      //1.主要用于标记配置类,兼备Component的效果。
@EnableScheduling   // 2.开启定时任务
@Slf4j
public class SaticScheduleTask {

    //3.添加定时任务
    //@Scheduled(cron = "0/5 * * * * ?")
    //或直接指定时间间隔,例如:5秒
    //每隔30秒发一条消息
    @Scheduled(fixedRate=30*1000)
    private void configureTasks() throws Exception{

        log.info("保持心跳");
        HashMap<String, WebSocket> stockStock = WebSocketStock.getStock();
        for (Map.Entry<String, WebSocket> stringWebSocketEntry : stockStock.entrySet()) {
            stringWebSocketEntry.getValue().sendMessage(stringWebSocketEntry.getKey(),"保持心跳");
        }
    }
}

后端工程启动 测试 首先启动前端项目 确保成功执行initWebsocket()

public static void main(String[] args){
	 HashMap<String, WebSocket> stockStock = WebSocketStock.getStock();
        for (Map.Entry<String, WebSocket> stringWebSocketEntry : stockStock.entrySet()) {
            stringWebSocketEntry.getValue().sendMessage(stringWebSocketEntry.getKey(),"这是一条测试数据");
        }
} 

运行看看前端有没有这条消息吧!

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

商朝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值