第一步首先搭建websocket服务器:
创建WebSocketProcess的类,因为有多台机器人,可能有多个websocket对象,可以看到,将每个websocket都存储到ConcurrentHashMap里,并且提供了发送到指定客户端和发送到全部客户端的方法

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Component
@ServerEndpoint(value = "/testWebsocket/{id}")
public class WebSocketProcess {

    /*
     * 持有每个webSocket对象,以key-value存储到线程安全ConcurrentHashMap,
     */
    private static ConcurrentHashMap<Long, WebSocketProcess> concurrentHashMap = new ConcurrentHashMap<>(12);

    /*private static WebSocketProcess webSocketProcess;
    @Autowired
    public void setWebSocketProcess(WebSocketProcess webSocketProcess) {
        WebSocketProcess.webSocketProcess = webSocketProcess;
    }*/
    /**
     * 会话对象
     **/
    private Session session;


    /*
     * 客户端创建连接时触发
     * */
    @OnOpen
    public void onOpen(Session session, @PathParam("id") long id) {
        //每新建立一个连接,就把当前客户id为key,this为value存储到map中
        this.session = session;
        concurrentHashMap.put(id, this);
        log.info("Open a websocket. id={}", id);
    }

    /**
     * 客户端连接关闭时触发
     **/
    @OnClose
    public void onClose(Session session, @PathParam("id") long id) {
        //客户端连接关闭时,移除map中存储的键值对
        concurrentHashMap.remove(id);
        log.info("close a websocket, concurrentHashMap remove sessionId= {}", id);
    }

    /**
     * 接收到客户端消息时触发
     */
    @OnMessage
    public void onMessage(String message, @PathParam("id") String id) {
        log.info("receive a message from client id={},msg={}", id, message);
    }

    /**
     * 连接发生异常时候触发
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("Error while websocket. ", error);
    }

    /**
     * 发送消息到指定客户端
     *  @param id
     *  @param message
     * */
    public void sendMessage(long id, String message) throws Exception {
        //根据id,从map中获取存储的webSocket对象
        WebSocketProcess webSocketProcess = concurrentHashMap.get(id);
        if (!ObjectUtils.isEmpty(webSocketProcess)) {
            //当客户端是Open状态时,才能发送消息
            if (webSocketProcess.session.isOpen()) {
                webSocketProcess.session.getBasicRemote().sendText(message);
            } else {
                log.error("websocket session={} is closed ", id);
            }
        } else {
            log.error("websocket session={} is not exit ", id);
        }
    }

    /**
     * 发送消息到所有客户端
     *
     * */
    public void sendAllMessage(String msg) throws Exception {
        log.info("online client count={}", concurrentHashMap.size());
        Set<Map.Entry<Long, WebSocketProcess>> entries = concurrentHashMap.entrySet();
        for (Map.Entry<Long, WebSocketProcess> entry : entries) {
            Long cid = entry.getKey();
            WebSocketProcess webSocketProcess = entry.getValue();
            boolean sessionOpen = webSocketProcess.session.isOpen();
            if (sessionOpen) {
               // webSocketProcess.session.getBasicRemote().sendText(msg);
                webSocketProcess.session.getBasicRemote().sendText(msg);
            } else {
                log.info("cid={} is closed,ignore send text", cid);
            }
        }
    }

}

第二步,在MyResourceServerConfigurerAdapter里(加了@EnableResourceServer注解的)开启路径权限,系统采用的oauth2做的权限认证,应该给websocket开启权限

注:@EnableResourceServer是为OAuth2资源服务器提供方便的注解,启用Spring Security 过滤器,通过传入的OAuth2令牌对请求进行身份验证。用户应添加此注释并提供一个ResourceServerConfigurer的@Bean (例如,通过 ResourceServerConfigurerAdapter),指定资源的详细信息(URL路径和资源ID)
在这里插入图片描述
这个testWebsocket就是上面WebSocketProcess里面的websocket路径
在这里插入图片描述

如果没有开启,会报拒绝访问的错org.springframework.security.access.AccessDeniedException: Access is denied
在这里插入图片描述

第三步:添加websocket的配置类:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

服务端基本搭建完成,然后就是在接口里面调用WebSocketProcess的发送方法,因为@Autowired无法注入WebSocketProcess,我之前写过可以通过SpringContextUtil工具类拿到WebSocketProcess对象
点击这里可以查看相关代码

然后就是在合适的地方调用这个方法:
在这里插入图片描述
两个参数,一个是id,一个是需要推送的报警信息

前端:

  var ws = new WebSocket('ws://127.0.0.1:8089/newRobot/testWebsocket/'+that.robotId+'');
    //var ws = new WebSocket('ws://127.0.0.1:12340/chat');
    
    //open event
    ws.onopen = function() {
        console.log("open websocket..."); 
    };
    
    //close event
    ws.onclose = function() {
        console.log("close websocket...");
    };
    
    // 响应onmessage事件:
    ws.onmessage = function(msg) {  
        //这里可以处理接收到服务器的数据
        console.log(msg); 
        
    

        let mes2="data:image/jpg;base64,"+msg.data; 
        that.openHTML(mes2);
    };
    
    function ws_send(){
        // 给服务器发送一个数字
        ws.send(document.getElementById('input_msg').value);
        
    }

openhtml是弹出方法,用的是elementUI的功能:

 openHTML(mes) {

        this.$refs.audio.currentTime = 0; //从头开始播放提示音
        this.$refs.audio.play(); //播放

        this.$message({
          type: "警报",
          dangerouslyUseHTMLString: true,
          message: '<img width="400" height="200" alt="star" src="'+mes+'"/>'
        });
      },

通过postman测试:
在这里插入图片描述

成功推送消息弹出图片

Logo

智屏生态联盟致力于大屏生态发展,利用大屏快应用技术降低开发者开发、发布大屏应用门槛

更多推荐