websocket

一、概念原理

 HTTP1.0 :一个 Request 一个 Response ,这次HTTP请求就结束了;

   HTTP1.1中进行了改进,使得有一个keep-alive,也就是说,在一个HTTP连接中,可以发送多个Request,接收多个Response。

   Websocket是一个持久化的协议,一次连接,永远有效,服务端就可以主动推送信息给客户端。

二、配置

@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        System.out.println("开启websocket支持。。。。。");
        return new ServerEndpointExporter();
    }
}

三、服务器类

   前端请求URL:

   ws://192.22.0.255:9999/person/websocket/alarm

 @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端,

 即 @ServerEndpoint 可以把当前类变成websocket服务类

//访问服务端的url地址
@ServerEndpoint(value = "/websocket/alarm")
@Component
public class SocketServer {
  private Session session; //与某个客户端的连接会话,需要通过它来给客户端发送数据
private static CopyOnWriteArraySet<WebSocket> webSocketSet = new CopyOnWriteArraySet<>();
    private static CopyOnWriteMap<String,CopyOnWriteArraySet<WebSocket>> webSocketMap = new CopyOnWriteMap<>();
RedisUtils redisUtils =SpringInitConfig.getBean("redisUtils"); //注入
 /**
     * 连接成功后调用的方法
     * @param session  可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
     */
    @OnOpen
public void onOpen(Session session) {
        this.session = session;
        webSocketSet.add(this);
        log.info("【websocket消息】有新的连接, 总数:{}", webSocketSet.size());
    }
  @OnClose  //连接关闭调用方法
    public void onClose() {
        webSocketSet.remove(this);
        log.info("【websocket消息】连接断开, 总数:{}", webSocketSet.size());
    }
 @OnMessage //接收到客户短消息
    public void onMessage(String message) {
        log.info("【websocket消息】收到客户端发来的消息:{}", message);
        OperateVo vo = new Gson().fromJson(message, OperateVo.class);
        if(OperateTypeConstants.AUTH.equals(vo.getOperateType())){
            if(webSocketMap.containsKey(vo.getSysid())){
                webSocketMap.get(vo.getSysid()).add(this);
            }else {
                CopyOnWriteArraySet<WebSocket> socketSessions = new CopyOnWriteArraySet<>();
                socketSessions.add(this);
                webSocketMap.put(vo.getSysid(),socketSessions);
            }
            //建立连接成功后,推送初始化告警数据
            //卡寻呼之外告警
            this.sendPepoleAlarm(this,"init",1);
            //卡寻呼告警
            this.sendCardCallAlarm(this,"init");
        }
    }
/**
     * 人员系统告警推送
     * @param alarmMessage
     */
    public void alarmBroadcast(WebSocket webSocket, String alarmMessage) {
        try {
            if(webSocket !=null){
                if (!webSocket.session.isOpen()) {
                } else {
                    try {
                        webSocket.session.getBasicRemote().sendText(alarmMessage);
                    } catch (IOException e) {
                        webSocket.session.close();
                        log.info("关闭websockt链接 wss:"+ session.toString());
                    }
                }
            }else{
                for (WebSocket webSockettemp: webSocketMap.get(SysConstants.PEPOLE)) {
                    try {
                        webSockettemp.session.getBasicRemote().sendText(alarmMessage);
                    } catch (Exception e) {
                        webSockettemp.session.close();
                    }
                }
            }
        }catch (Exception e){
            log.info("关闭websockt链接 wss:"+ e.getMessage());
        }finally {
        }
    }
    public void sendPepoleAlarm(WebSocket session, String commandType, Integer dataSendType) {
        List<AlarmVo> result = new ArrayList<>();
        Gson gson = new Gson();
        List<Object> allAlarmList = redisUtils.hValues(RedisConstants.ALARM);
        if(allAlarmList != null && allAlarmList.size() >0){
            for(Object alarm:allAlarmList){
                AlarmListDto alarmListDto = gson.fromJson(alarm.toString(), AlarmListDto.class);
                AlarmVo vo = new AlarmVo();
                BeanUtils.copyProperties(alarmListDto,vo);
                result.add(vo);
            }
        }

        Map<String,Object> resultMap = new HashMap<>();
        resultMap.put("dataSendType",dataSendType);
        resultMap.put("alarmDataType","alarm");
        resultMap.put("commandType",commandType);
        resultMap.put("alarmdata",result);
        this.alarmBroadcast(session,JSON.toJSONString(resultMap));
    }

    public void sendCardCallAlarm(WebSocket session, String commandType) {
        List<AlarmVo> result = new ArrayList<>();
        List<Object> allAlarmList = redisUtils.hValues(RedisConstants.CARDCALL_ALARM);
        if(allAlarmList !=null && allAlarmList.size()>0){
            Gson gson = new Gson();
            for(Object alram:allAlarmList){
                AlarmVo vo = gson.fromJson(alram.toString(),AlarmVo.class);
                result.add(vo);
            }
        }
        Map<String,Object> resultMap = new HashMap<>();
        resultMap.put("alarmDataType","cardCall");
        resultMap.put("commandType",commandType);
        resultMap.put("alarmdata",allAlarmList);
        resultMap.put("alarmdata",result);
        this.alarmBroadcast(session, JSON.toJSONString(resultMap));
    }
}

 

例二:

@Service
@ServerEndpoint("/websocket/review")
@Slf4j
public class ReviewWebSocket {
    private VehicleApplicationOrderServiceImpl orderService = SpringInitConfig.getBean("vehicleApplicationOrderService");
    private UserSoundServiceImpl soundService = SpringInitConfig.getBean("userSoundService");
    private Session session;
    /**
     * 是调度员登录的所有客户端连接
     */
    private static CopyOnWriteArraySet<ReviewWebSocket> reviewWebSockets = new CopyOnWriteArraySet<>();
    private String empNo;

    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        log.info("【websocket消息】有新的连接, 总数:{}", reviewWebSockets.size());
    }

    @OnClose
    public void onClose() {
        reviewWebSockets.remove(this);
        log.info("【websocket消息】连接断开, 总数:{}", reviewWebSockets.size());
    }

    @OnMessage
    public void onMessage(String message) {
        log.info("【websocket消息】收到客户端发来的消息:{}", message);
        OperateVo vo = new Gson().fromJson(message, OperateVo.class);
        if (orderService.isDispatcher(vo.getEmpNo())) { // 是调度员
            if (!reviewWebSockets.contains(this)) {
                this.empNo = vo.getEmpNo();
                reviewWebSockets.add(this);
                log.info("有新的调度员客户端链接加入");
                sendMessage(this, soundService.getSoundStatus(vo.getEmpNo()), 0); // true要播放声音 0不用请求数据
            } else {
                soundService.addSoundOrUpdate(vo.getEmpNo(), vo.isSoundStatus(), vo.getSoundTime()); // 声音新增或更新
                sendMessage(this, soundService.getSoundStatus(vo.getEmpNo()), 0); //返回声音的状态 0不用请求数据
            }
            log.info("调度员客户端链接个数:{}", reviewWebSockets.size());
        }
    }

    /**
     * 发送消息
     *
     * @param webSocket   链接
     * @param soundStatus 声音的状态
     * @param status      是否请求数据 0不用请求数据 1新增数据 2减少数据
     */
    public void sendMessage(ReviewWebSocket webSocket, boolean soundStatus, int status) {
        log.info("存在要审核的订单,给调度员发广播,声音状态:{}", soundStatus);
        try {
            Map<String, Object> resultMap = new HashMap<>();
            resultMap.put("sound", soundStatus); // 声音状态
            resultMap.put("soundTime", soundService.getSoundTime(webSocket.empNo)); // 声音提醒间隔,单位秒
            resultMap.put("status", status); // 前端是否要刷新数据
            resultMap.put("reviewData", orderService.existReviewSendMessage()); // 是否存在要审核的数据
            webSocket.session.getBasicRemote().sendText(JSON.toJSONString(resultMap));
        } catch (Exception e) {
            e.printStackTrace();
            log.info("【websocket消息】广播消息异常:{}", e.getMessage());
        }
    }

    /**
     * 调度员审核派车推送
     *
     * @param status 1增加 2减少
     */
    public void reviewSendBroadcast(int status) {
        log.info("有新的未审核订单,给所有调度员客户端发广播");
        try {
            for (ReviewWebSocket webSocket : reviewWebSockets) {
                log.info("客户端声音状态:{}", soundService.getSoundStatus(webSocket.empNo));
                Map<String, Object> resultMap = new HashMap<>();
                resultMap.put("sound", soundService.getSoundStatus(webSocket.empNo)); // 声音状态
                resultMap.put("soundTime", soundService.getSoundTime(webSocket.empNo)); // 声音提醒间隔,单位秒
                resultMap.put("status", status); // 前端是否要刷新数据
                resultMap.put("reviewData", orderService.existReviewSendMessage()); // 是否存在要审核的数据
                webSocket.session.getBasicRemote().sendText(JSON.toJSONString(resultMap));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 一般收到消息后经处理后要发送消息

收消息:

    @OnMessage
    public void onMessage(String message) { }

   发消息:

    webSocket.session.getBasicRemote().sendText(message);

四、注意

      在@ServerEndpoint注解类中使用@Resource等注入失败。报出空指针异常。

原理是WebSocket是线程安全的,有用户连接时就会创建一个新的端点实例,一个端WebSocket是多对象的,使用的spring却是单例模式。这两者刚好冲突

@Autowired注解注入对象是在启动的时候就把对象注入,而不是在使用A对象时才把A需要的B对象注入到A中。

而WebSocket在刚刚有说到,有连接时才实例化对象,而且有多个连接就有多个对象。总结就是,WebSocket是多对象的。不管单独使用也好,结合spring也好,或者结合SpringBoot也罢,他都是多对象的。

解决:使用从容器中取对象的工具类

posted @ 2021-01-11 09:25  zhangtianhong511  阅读(161)  评论(0)    收藏  举报