范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

SpringBootVueWebsocket实现服务器端向客户端主动发送消息

  概述
  本文通过一个实际的场景来介绍在前后端分离的项目中通过 WebSocket 来实现服务器端主动向客户端发送消息的应用。主要内容如下 WebSocket 是什么 服务器端 向 客户端 主动发送消息的案例说明 SpringBoot 后端中 Websocket 的配置和使用 后端 Websocket 实现原理 Vue 前端 Websocket 的配置和使用 WebSocket 是什么
  Websocket 是一种在单个 TCP 连接上进行全双工通信的协议。WebSocket 连接成功后,服务端与客户端可以双向通信。在需要消息推送的场景,Websocket 相对于轮询能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
  具体如下特点 与 HTTP 协议有着良好的兼容性。默认端口也是 80 和 443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。 依赖于 TCP 协议 数据格式比较轻量,性能开销小,通信高效。 可以发送文本,也可以发送二进制数据。 没有同源限制,客户端可以与任意服务器通信。 协议标识符是 ws(如果加密,则为 wss),服务器网址就是 URL 服务器端 向 客户端 主动发送消息的案例说明
  在客户端的列表数据中有个 status 字段,服务器端需要花费较长的时间进行处理,处理完成后才会更新对应数据的 status 字段值,通过 Websocket 的处理流程如下: 前端页面列表数据加载后,初始化一组 Websocket 客户端对象 服务器端 接收到 前端数据状态的查询请求 服务器端 每隔一段时间查询一下数据库,然后返回给客户端 客户端 根据返回的数据状态,再更新页面数据 后端 SpringBoot 中 Websocket 的配置和使用Maven 依赖    org.springframework.boot    spring-boot-starter-websocket  配置
  通过注入 ServerEndpointExporter 类,用于在项目启动的时候自动将使用了 @ServerEndpoint 注解声明的 Websocket endpoint 注册到 WebSocketContainer 中。 package com.ckjava.config;  /**  * Function:  *  * @author chenkui 2022/4/6 17:55  */  import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.server.standard.ServerEndpointExporter;  @Configuration public class WebSocketConfig {     /**      * 注入 ServerEndpointExporter,      * 这个 bean 会自动注册使用了 @ServerEndpoint 注解声明的 Websocket endpoint      */     @Bean     public ServerEndpointExporter serverEndpointExporter() {         return new ServerEndpointExporter();     }  } 通过@ServerEndpoint注解标注实现类通过在类上增加 @ServerEndpoint 和 @Component 注解,用于标注 Websocket 的实现类 通过 ConcurrentHashMap 管理多个客户端的 Session 通过 ScheduledExecutorService 和 init 方法实现定时对客户端进行消息发送 @OnOpen 标注的方法,用于接收客户端的 连接请求,其中的 @PathParam 用于接收 url 中的参数 @OnClose 标注的方法,用于接收客户端的 关闭请求。 @OnMessage标注的方法,用于接收客户端的 消息。 @OnError标注的方法,用于错误处理 package com.ckjava.websocket;  import com.ckjava.xutils.JsonUtils; import com.ckjava.entity.TSysPubEntity; import com.ckjava.service.TSysPubService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component;  import javax.annotation.PostConstruct; import javax.annotation.Resource; import javax.websocket.*; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference;  /**  * Function:  *  * @author chenkui 2022/4/6 18:00  */ @Slf4j @Component @ServerEndpoint("/websocket/{pubId}") public class PubStatusWS {     /**      * 每个数据对应一个 session      */     private static final ConcurrentHashMap sessionPool = new ConcurrentHashMap<>();     /**      * 定时任务的线程池      */     private static final ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1);      @Resource     private TSysPubService pubService;      /**      * 定时从数据库中获取数据的最新状态,然后返回给前端      */     @PostConstruct     public void init() {         scheduledExecutorService.scheduleAtFixedRate(() -> {             sessionPool.forEach((pubId, session) -> {                 final TSysPubEntity entity = pubService.findById(pubId);                  // 返回最新的状态                 session.getAsyncRemote().sendText(JsonUtils.toJSONString(entity));                 log.debug(String.format("websocket消息 server send to pubId %s, text:%s", session.getId(), entity.getPubStatus()));             });         }, 10, 30, TimeUnit.SECONDS);     }      @OnOpen     public void onOpen(final Session session, final EndpointConfig endpointConfig,                        @PathParam(value = "pubId") final Integer pubId) {         sessionPool.put(pubId, session);         log.info(String.format("【websocket消息】 pubId:%s 加入连接,当前总数为:%s", pubId, sessionPool.size()));     }      @OnClose     public void onClose(final Session session, final CloseReason closeReason) {         final AtomicReference atomicReference = new AtomicReference<>();         sessionPool.forEach((pubId, s) -> {             if (Objects.equals(s.getId(), session.getId())) {                 atomicReference.set(pubId);             }         });          sessionPool.remove(atomicReference.get());         log.info(String.format("【websocket消息】pubId:%s 连接断开,当前总数为:%s", atomicReference.get(), sessionPool.size()));     }      @OnMessage     public void onMessage(final Session session, final String message) {         log.error(String.format("【websocket消息】收到客户端 pubId:%s 消息:%s", session.getId(), message));     }      @OnError     public void onError(final Session session, final Throwable throwable) {         log.error(String.format("【websocket消息】pubId:%s 出现异常:%s", session.getId(), throwable));     }      /**      * 向客户端群发消息      * @param message 文本消息      */     public void sendAllMessage(final String message) {         sessionPool.forEach((pubId, session) -> {             session.getAsyncRemote().sendText(message);         });     }      /**      * 向某个客户端发送消息      *      * @param pubId 客户端id      * @param message 文本消息      */     public void sendOneMessage(final Integer pubId, final String message) {         final Session session = sessionPool.get(pubId);         if (session != null) {             session.getAsyncRemote().sendText(message);         }     }   } 后端 Websocket 实现原理
  为什么增加一个 ServerEndpointExporter Bean,并通过在一个类上增加 @ServerEndpoint 和 @Component 注解就可以实现服务器端 Websocket 功能,这里简单解析一下。 ServerEndpointExporter 的核心方法ServerEndpointExporter 实现了 spring 中的 SmartInitializingSingleton 接口,并重写了 afterSingletonsInstantiated 方法,具体如下 @Override public void afterSingletonsInstantiated() { 	registerEndpoints(); } 在 registerEndpoints 方法中可以发现,通过 ApplicationContext 中的 getBeanNamesForAnnotation 方法,从 spring 的 ioc 容器中获取含有 @ServerEndpoint 注解的类。 /**  * Actually register the endpoints. Called by {@link #afterSingletonsInstantiated()}.  */ protected void registerEndpoints() { 	Set> endpointClasses = new LinkedHashSet<>(); 	if (this.annotatedEndpointClasses != null) { 		endpointClasses.addAll(this.annotatedEndpointClasses); 	}  	ApplicationContext context = getApplicationContext(); 	if (context != null) { 		String[] endpointBeanNames = context.getBeanNamesForAnnotation(ServerEndpoint.class); 		for (String beanName : endpointBeanNames) { 			endpointClasses.add(context.getType(beanName)); 		} 	}  	for (Class<?> endpointClass : endpointClasses) { 		registerEndpoint(endpointClass); 	}  	if (context != null) { 		Map endpointConfigMap = context.getBeansOfType(ServerEndpointConfig.class); 		for (ServerEndpointConfig endpointConfig : endpointConfigMap.values()) { 			registerEndpoint(endpointConfig); 		} 	} } 在 registerEndpoint 方法中,通过 ServerContainer 的 addEndpoint 方法,最终将 endpoint 实现类注册到 ServerContainer 中。 private void registerEndpoint(Class<?> endpointClass) { 	ServerContainer serverContainer = getServerContainer(); 	Assert.state(serverContainer != null, 			"No ServerContainer set. Most likely the server"s own WebSocket ServletContainerInitializer " + 			"has not run yet. Was the Spring ApplicationContext refreshed through a " + 			"org.springframework.web.context.ContextLoaderListener, " + 			"i.e. after the ServletContext has been fully initialized?"); 	try { 		if (logger.isDebugEnabled()) { 			logger.debug("Registering @ServerEndpoint class: " + endpointClass); 		} 		serverContainer.addEndpoint(endpointClass); 	} 	catch (DeploymentException ex) { 		throw new IllegalStateException("Failed to register @ServerEndpoint class: " + endpointClass, ex); 	} } ServerContainer
  java 定义了一套 javax.servlet-api, 一个 HttpServlet 就是一个 HTTP 服务。java websocket 并非基于 servlet-api 简单扩展, 而是新定义了一套 javax.websocket-api。
  一个 websocket 服务对应一个 Endpoint。与 ServletContext 对应, websocket-api 也定义了 WebSocketContainer, 而编程方式注册 websocket 的接口是继承自 WebSocketContainer 的 ServerContainer。
  一个 websocket 可以接受并管理多个连接, 因此可被视作一个 server。主流 servlet 容器都支持 websocket, 如 tomcat, jetty 等。看 ServerContainer api 文档, 可从 ServletContext attribute 找到 ServerContainer。 Vue 前端 Websocket 的配置和使用在 created 方法中调用了 getPageData 方法,用于接收到列表数据后,通过 initWs 方法 给 每个数据 id 初始化一个 WebSocket 客户端 通过 onerror 事件绑定 出现异常 时候的回调方法 通过 onopen 事件绑定 连接成功 的回调方法 通过 onmessage 事件绑定 接收到服务器端消息 的回调方法,这里收到消息后,再更新前端的数据 通过 onclose 事件绑定 关闭连接 的回调方法 在 unmounted 方法中调用了 onbeforeunload 方法,用于关闭所有的 WebSocket 连接

亚马逊和其它跨境电商平台的对比很多跨境电商卖家不知道如何选择销售平台,我整理了目前几大主流电商平台的发展情况,供大家借鉴1eBay可以说,eBay是中国跨境电商卖家发源之根。很多现在我们耳熟能详的跨境电商大卖家AppleWatchSeries7预售开始了!一篇文章告诉你该不该入手十一假期,苹果宣布AppleWatchSeries7将于10月8日8点开始接受预定,10月15日发售。相信不少朋友已经等不及想要入手了,那么,AppleWatchSeries7(以多平台用户的音频现阶段最好的选择雷蛇Barracuda梭鱼X游戏耳机作为游戏用户外设的更迭永无止尽,桌面的设备已经都已更新无线设备,但还是缺少一款随身高性能的游戏耳机,来满足掌机以及手机的游戏需求。不过雷蛇最近推出了全新的Barracuda梭鱼X无李书福为什么要造手机?文林淑华微信公众号个人汽车汽车智能化快把传统车企逼疯了,李书福都要去造手机了。哪怕风险大到不能拿上市公司冒险,只能以老板个人名义做。还是亲自下场做,不是蜻蜓点水随便投资一下。造手机第三季度中东欧市场5G手机报告出炉,小米拿下第一,遥遥领先友商肉眼可见的进步,小米再拿下一城,在海外市场用户已经逐渐接受小米手机了。第三季度中东欧5G智能手机市场数据结果公布了,国产厂商已然成为主力。一枝独秀不是春,百花齐放春满园,手机行业经买国产手机就是爱国,买苹果手机就是不爱国?买手机不是自由的吗我说我不喜欢用国产手机,结果很多人来喷我,你说这个世道都怎么了?我们常常说的开放平等,难道都成了摆设吗?用什么手机不是个人的自由吗?难道组装外国手机的工人,都没给他们发工资吗?还是蓝牙耳机玩游戏延迟太高?锐可余音开拓者揭露蓝牙耳机行业秘密随着智能手机越来越轻薄的同时,不少手机厂商都在逐步取消传统有线3。5mm耳机孔,这个行为也让这几年TWS无线蓝牙耳机开始流行。蓝牙耳机摆脱了传统线材的束缚,更加方便携带,以至于今天音质悦耳动听佩戴稳固舒适,蓝牙耳机我选Tribit蓝牙耳机对于人们来说并不陌生,和有线耳机相比,蓝牙耳机体型要更为小巧一些,而且便于携带,容易收纳,怪不得会受到人们的青睐呢!最近我也准备入手一款蓝牙耳机,因此做了不少功课,对比了很华强北的苹果耳机,太逆天了相信机友们都知道,这几年AirPods已经成为苹果所有业务当中,利润率较高的一个。前段时间有调查机构专门做了评估,去年苹果光是靠它,就赚了230。5亿美元,将近1500亿元人民币。干货大型分布式系统中缓存理论及应用场景本文主要介绍大型分布式系统中缓存的相关理论,常见的缓存组件以及应用场景。缓存概述缓存的分类缓存主要分为四类,如下图CDN缓存CDN(ContentDeliveryNetwork内容如何在不兼容设备上成功安装Windows11系统出于安全方面的考量,Windows11提高了系统的最低硬件标准。除了最受关注的TPM2。0规格和处理器要求之外,还对UEFI启动提出了新的要求,以及需要在GPT分区格式中进行安装(
有一说一,中国移动独资建700MHz5G网络,是一个三赢的选择9月10日,中国移动有限公司发布了与中国广播电视网络集团有限公司订立之有关5G共建共享补充协议。主要协议内容如下1)共建共享700MHz无线网络双方围绕中国广电获得的国家相关部门授在家看电视就能享受影院画质音质,一台华为V75Super就够了越来越多的年轻人,选择在家里观看高清影视作品,作为下班后周末时的休闲放松方式。当然,年轻人对画质音质的要求也很高。为此,许多人购买大型投影,还有昂贵的家庭影院。这样的操作,需要家里传音InfinixZeroX真机曝光,星空后盖1亿像素主摄近日,爆料人士gsmarena正式带来了InfinixZeroX真机实拍图片。从曝光图片来看,InfinixZeroX背面采用星空后盖,整体观感十分深邃。整机采用后置三摄方案,包括戴尔14英寸笔记本灵越14Pro很能打,配MX450和i711370H提起一款14英寸笔记本应该什么样,以前的传统印象是15W低电压处理器,配个MX350独显,就可以了。但在今年,不仅很多笔记本配上了MX450独显,处理器也进一步提升,变成标压处理器静音无雾还杀菌,阿尔卡司T700加湿器实测今年的夏天感觉特别的短暂,本以为立秋之后还会热一段时间,没想到接连的几场秋雨,让这个城市慢慢有了一丝凉意。出生于海边城市的我在这个内陆城市呆了十多年了,依旧习惯不了它的干燥,早上起静音但强劲的洗衣机,非西门子洗衣机莫属今天给大家安利一个接地气的好东西,它是每家每户必备的,有了它,生活一定会加分。那今天我要说的是什么呢?其实就是我们每家每户都必备的洗衣机,随着科技的发展,洗衣机也出现了很多功能,那彩票代码VBA写的本代码真正的大数据,大过滤,几百万次的计算可能只有一个号码,甚至一个都没有,可见,通过反复重算,光循环就是百万每次每位,每位进行2次循环,本代码会进入双色球,原理是一样的,几百万计美团做人做事完全不考虑骑手你们看看这美团的配送时间,抢单是1102分,顾客期望送达时间111418,平台考核时间1122,就给骑手20分钟的配送时间,我怎么可能配送的了?又怎么可能不超时?我第一时间打美团客有一种排插,叫无线排插,一经发布,大牌都不吃香了,实用随着电力进入千家万户,插线板这种产品早已变成了一种刚需。厨房需要客厅需要卧室需要只要是有电的地方就少不了插排的身影!但是传统的排插也渐渐满足不了我们日常的使用!相信很多读者也会跟我python音乐可视化好玩的matplotlib南丁格尔玫瑰图版效果图操作演示技术要点1matplotlib的南丁格尔玫瑰图,用极坐标polar制作,并动画显示。2pygame新版的播放mp3,但本机的操作系统不能播放mp3,我用pydub做些被北上广深排挤的共享电单车,为何在三四线城市如此受欢迎?众所周知,北上广深这些城市里我们只看到共享单车,却不见共享电动车,这是为何。其实也有不少品牌有悄悄的投放在这些城市的一些小镇区以及比较偏的地方,但都无一例外,被约谈被清退。我国多个