浅谈一下个人对webscoket的使用

本文介绍了WebSocket在网络通信中的应用,特别是在聊天场景中的作用。通过展示WebSocket配置类、工具类的实现,以及心跳包的处理和消息推送流程,阐述了后端如何与前端小程序配合,实现实时通信。当设备触发事件时,后端接收到通知并使用WebSocket向用户推送消息。此外,还提到前端每隔10秒发送心跳包,保持连接活跃,超过120秒未收到则断开连接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

webscoket这个一般是运用于聊天场景,我之前做一个项目内容需求是这样的,后端对接一个设备,这个设备新增一个事件的时候会调用后端的接口,然后后端需要发送一个webscoket通知给小程序端用户让他们知悉,话不多说直接上代码。

1、websocket配置类

@Configuration
public class WebSocketConfig {

  /**
   * 注入一个ServerEndpointExporter,该Bean会自动注册使用@ServerEndpoint注解申明的websocket endpoint
   */
  @Bean
  public ServerEndpointExporter serverEndpointExporter() {
    return new ServerEndpointExporter();
  }
}

2、webscoket工具类

 这边前端小程序登录之后需要每隔10s通过webscoket发送一次心跳包给我以证实其在线,我这里规定超过120s没接收到心跳消息即主动断开webscoket连接。使用的是onMessage这个方法

 

 

 



/**
 * <p>
 * websocket服务
 * </p>

@ServerEndpoint(value = "/websocket/{userId}")
@Component
public class WebSocketService {


  private  static  EventService eventService;


  static {
    //从 Spring 容器中 获取 startFlowService 对象
    eventService = SpringContextUtil.getBean(EventService.class);
  }

  private static final Logger log = LoggerFactory.getLogger(WebSocketService.class);

  /**
   * 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
   */
  private static AtomicInteger onlineCount = new AtomicInteger(0);

  /**
   * concurrent包的线程安全Set,用来存放每个客户端对应的WebSocketServer对象。
   */
  private static ConcurrentHashMap<String, WebSocketClient> webSocketMap = new ConcurrentHashMap<>();

  /**
   * 与某个客户端的连接会话,需要通过它来给客户端发送数据
   */
  private Session session;

  /**
   * 接收userId
   */
  private String userId = null;

  /**
   * 连接建立成功调用的方法
   */
  @OnOpen
  public void onOpen(Session session, @PathParam("userId") String userId) {
    if (!webSocketMap.containsKey(userId)) {
      onlineCount.incrementAndGet(); // 在线数 +1
    }
    this.session = session;
    this.userId  = userId;

    WebSocketClient client = WebSocketClient.builder()
        .session(session)
        .uri(session.getRequestURI().toString())
        .userId(userId)
        .lastTime(new Date())
        .build();
    webSocketMap.put(userId, client);

    log.info("用户连接:" + userId + ",当前连接用户数为:" + onlineCount.get());
    System.out.println("连接建立---------------------------------------------------------------------------");
  }

  /**
   * 连接关闭调用的方法
   */
  @OnClose
  public void onClose() {
    if (webSocketMap.containsKey(userId)) {
      webSocketMap.remove(userId);
      // 在线数减1
      onlineCount.decrementAndGet();
    }
    log.info(userId + "用户退出,当前在线人数为:" + onlineCount.get());
  }

  /**
   * 收到客户端消息后调用的方法
   *
   * @param message 客户端发送过来的消息
   */
  @OnMessage
  public void onMessage(String message) throws Exception {
    int  deviationValue = 120;
    Date now            = new Date();
    if (CollectionUtils.isEmpty(webSocketMap)) {
      //没有连接时处理
      log.error("小程序心跳-连接池为空,非正常连接用户:" + userId);
    }

    if (!webSocketMap.containsKey(userId)) {
      //没有连接时处理
      log.error("小程序心跳-此用户不存在连接" + userId);
    }
    WebSocketClient webSocketClient = webSocketMap.get(userId);
    //判断连接是否已超时
    if (DateUtil.adjustDateBySecond(webSocketClient.getLastTime(), deviationValue).compareTo(now)
        < 0) {
      //断开连接
     onClose();
    } else {
      //更新时间
      webSocketClient.setLastTime(now);
      log.info("收到用户:" + userId + ",消息:" + message);
    }
  }


  /**
   * 连接错误处理
   *
   * @param session
   * @param error
   */
  @OnError
  public void onError(Session session, Throwable error) {
    log.error("用户错误:" + this.userId + ",原因:" + error.getMessage());
    error.printStackTrace();
  }

  /**
   * 关闭指定用户的连接
   *
   * @param userId 用户标识
   */
  public void closeConnect(String userId) {
    if (webSocketMap.containsKey(userId)) {
      webSocketMap.remove(userId);
      // 在线数减1
      onlineCount.decrementAndGet();
    }
    log.info(userId + "用户退出,当前在线人数为:" + onlineCount.get());
  }

  /**
   * 连接服务器成功后主动推送
   */
  public void sendMessage(String message) throws IOException {
    synchronized (session) {
      this.session.getBasicRemote().sendText(message + "------------------" + session);
    }
  }

  /**
   * 向指定客户端发送消息
   *
   * @param
   * @param
   */
  public static void sendInfo(String userId, String message) {
    log.info("推送给" + userId + ",内容是:" + message);
    try {
      WebSocketClient webSocketClient = webSocketMap.get(userId);
      if (webSocketClient != null) {
        webSocketClient.getSession().getBasicRemote().sendText(message);
      }
    } catch (IOException e) {
      e.printStackTrace();
      throw new RuntimeException(e.getMessage());
    }
  }


  public static ConcurrentHashMap<String, WebSocketClient> getWebSocketMap() {
    return webSocketMap;
  }

  public static void setWebSocketMap(ConcurrentHashMap<String, WebSocketClient> webSocketMap) {
    WebSocketService.webSocketMap = webSocketMap;
  }

  public Session getSession() {
    return session;
  }

  public void setSession(Session session) {
    this.session = session;
  }

  public String getuserId() {
    return userId;
  }

  public void setuserId(String userId) {
    this.userId = userId;
  }
}

3、实体类


/**
 * 
 *

 * @date 2021-11-22 14:28:15
 */
@Data
@TableName("ar_event")
public class EventEntity  implements Serializable {
 private static final long serialVersionUID = 1L;


 /**
  *事件id
  */
 @TableId
 private Integer eventId;
 /**
  *设备id
  */
 @NotNull( message = "设备id不为空")
 private Integer id;
 /**
  * 事件类型
  */
 @NotNull( message = "事件类型不为空")
 @Max(value = 15)
 private Integer eventtype;
 /**
  * 二级事件类型,只有eventTypeId=0或14使用
  */
 private String eventtype2;
 /**
  * 事件图片base64格式。
  * 人脸抓拍和体温测试必填
  */
 private String image;
 /**
  * 目前只有eventTypeId=13使用
  */
 private String image2;
 /**
  * 事件发生时间
  */
 @NotNull(message = "事件发生时间不能为空")
 private Date eventtime;
 /**
  * 姓名
  */
 private String name;
 /**
  * 相似度
  */
 private Float score;
 /**
  * 体温
  */
 private String celsius;
 /**
  * 体温状态
  * 0:异常 1:正常
  */
 private Integer status;
 /**
  * 是否戴口罩
  * 0:否  1:是,2未检测
  */
 private Integer mask;
 /**
  *预留字段json格式
  */
 private String extra;
 /**
  * 事件阅读状态,0:未读 1:已读
  */
 private Integer isRead;
}

4、推送相关消息

@Override
public int insertEvent(EventEntity eventEntity) {
  Date now            = new Date();
  int  deviationValue = 120;
  int id = eventEntity.getId();
  int app_id = deviceDao.selectBydId(id);
  //TODO 获取ar用户和小程序用户的关联 从而获取小程序用户id
  String app_user_id = String.valueOf(app_id);

  //websocket连接池
  ConcurrentHashMap<String, WebSocketClient> webSocketMap = WebSocketService.getWebSocketMap();

  //连接池为空 或不存在连接 不推送事件
  if (CollectionUtils.isEmpty(webSocketMap) || !webSocketMap.containsKey(app_user_id)) {
    return insert(eventEntity);
  }

  //判断连接是否已过期 过期不推送事件
  WebSocketClient webSocketClient = webSocketMap.get(app_user_id);
  if (DateUtil.adjustDateBySecond(webSocketClient.getLastTime(), deviationValue).compareTo(now)
      < 0) {
  return insert(eventEntity);
}
  int res = insert(eventEntity);
  if(res == 1){
    //推送事件到前端
    Map<String,Object> map = new HashMap<>(3);
    map.put("user_id",app_user_id);
    map.put("event_id",eventEntity.getEventId());
    WebSocketService.sendInfo(app_user_id, JSON.toJSONString(map));
  }else{
    log.error("新增失败");
    return 0;
  }
  return res;
}

大体流程就是这样,前端相关我就不发布了,如果有困惑的码友可以私信我。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值