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;
}
大体流程就是这样,前端相关我就不发布了,如果有困惑的码友可以私信我。
WebSocket在聊天场景中的应用实践
本文介绍了WebSocket在网络通信中的应用,特别是在聊天场景中的作用。通过展示WebSocket配置类、工具类的实现,以及心跳包的处理和消息推送流程,阐述了后端如何与前端小程序配合,实现实时通信。当设备触发事件时,后端接收到通知并使用WebSocket向用户推送消息。此外,还提到前端每隔10秒发送心跳包,保持连接活跃,超过120秒未收到则断开连接。
1271

被折叠的 条评论
为什么被折叠?



