Spring clound+VUE+WebSocket实现消息推送 一(即时通讯)
需求:
当用户进行某个操作,需要通知其他用户时,其他用户可以实时接收消息。
工程为 Spring cloud + VUE 。
技术实现:
Spring Cloud、Spring boot、vue、webSocket等。
流程
用户登录系统→连接到socket服务进行监听→页面用户操作某业务→调用socket服务传递json消息→socket服务推送消息到其他用户页面→收到消息解析json进行其他业务处理
通讯json
目前推送的有:业务的ID、业务的类型、推送的简约内容
============================新建服务====================================================
一:新建socket服务,聚合到父工程里(不会的去复习下spring boot)
1:配置socket属性文件application.yml,兼容后面文章的离线通讯
#eureka注册中心地址
eureka:instance:prefer-ip-address: trueclient:service-url:defaultZone: http://XXX.XXX.XXX.XXX:8060/eureka/register-with-eureka: truefetch-registry: trueserver:port: 9002servlet:context-path: /
spring:application:name: api-webSocketdatasource:driver-class-name: oracle.jdbc.OracleDriverurl: jdbc:oracle:thin:@XXX.XXX.XXX:8098:XEusername: XXXpassword: XXXtype: com.alibaba.druid.pool.DruidDataSource#最大活跃数maxActive: 20#初始化数量initialSize: 5#最大连接等待超时时间maxWait: 60000redis:host: XXX.XXX.XXX.XXX# 端口port: 6379# 超时时间timeout: 5000jedis:pool:#连接池最大阻塞等待时间(使用负值表示没有限制)max-wait: 3000#连接池最大连接数(使用负值表示没有限制)max-active: 200#连接池中的最大空闲连接max-idle: 20#连接池中最小空闲连接min-idle: 2# MyBatis
mybatis:# 搜索指定包别名typeAliasesPackage: com.fencer.rcdd# 配置mapper的扫描,找到所有的mapper.xml映射文件mapperLocations: classpath*:mapper/**/*Mapper.xml# 日志配置
logging:level:com.fencer: debugorg.springframework: WARNorg.spring.springboot.dao: debug
2:父工程添加相关依赖pom.xml
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId><version>2.0.4.RELEASE</version></dependency>
3:新建socket配置类
package com.XXX.io.ws;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/*** 类描述:** @author :carry* @version: 1.0 CreatedDate in 2019年09月24日* <p>* 修订历史: 日期 修订者 修订描述*/
@Configuration
public class WebSocketConfig {/** * @方法描述: 注入ServerEndpointExporter,* * 这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint* @return: org.springframework.web.socket.server.standard.ServerEndpointExporter* @Author: carry*/@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
}
4:新建服务类
package com.fencer.io.ws;import org.springframework.stereotype.Component;import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;/*** 类描述:** @author :carry* @version: 1.0 CreatedDate in 2019年09月24日* <p>* 修订历史: 日期 修订者 修订描述*/
@Component
@ServerEndpoint(value = "/websocket/{userId}")//设置访问URL
public class WebSocket {//静态变量,用来记录当前在线连接数。private static int onlineCount = 0;private Session session;//concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。private static CopyOnWriteArraySet<WebSocket> webSockets = new CopyOnWriteArraySet<>();//与某个客户端的连接会话,需要通过它来给客户端发送数据private static Map<String, Session> sessionPool = new HashMap<String, Session>();/*** @方法描述: 开启socket* @return: void* @Author: carry*/@OnOpenpublic void onOpen(Session session, @PathParam(value = "userId") String userId) {this.session = session;webSockets.add(this);//加入set中addOnlineCount(); //在线数加1sessionPool.put(userId, session);//把对应用户id的session放到sessionPool中,用于单点信息发送System.out.println("【websocket消息】 有新连接加入!用户id" + userId + ",当前在线人数为" + getOnlineCount());}/*** @方法描述: 关闭socket* @return: void* @Author: carry*/@OnClosepublic void onClose() {webSockets.remove(this);subOnlineCount(); //在线数减1System.out.println("【websocket消息】 连接断开!当前在线人数为" + getOnlineCount());}/*** @方法描述: 收到客户端消息* @return: void* @Author: carry*/@OnMessagepublic void onMessage(String message) {System.out.println("【websocket消息】收到客户端消息:" + message);}/*** @方法描述: 广播消息全体发送* @return: void* @Author: carry*/public void sendAllMessage(String message) {for (WebSocket webSocket : webSockets) {System.out.println("【websocket消息】广播消息:" + message);try {webSocket.session.getAsyncRemote().sendText(message);} catch (Exception e) {e.printStackTrace();}}}/*** @方法描述: 一对一单点消息* @return: void* @Author: carry*/public void sendOneMessage(String userId, String message) {try {// 防止推送到客户端的信息太多导致弹窗太快Thread.sleep(500);System.out.println("用户"+userId+"【websocket消息】单点消息:" + message);Session session = sessionPool.get(userId);if (session != null) {// getAsyncRemote是异步发送,加锁防止上一个消息还未发完下一个消息又进入了此方法// 也就是防止多线程中同一个session多次被调用报错,虽然上面睡了0.5秒,为了保险最好加锁synchronized (session) {session.getAsyncRemote().sendText(message);}}} catch (Exception e) {e.printStackTrace();}}/*** @方法描述: 发生错误时调用* @return: void* @Author: carry*/@OnErrorpublic void onError(Session session, Throwable error) {System.out.println("发生错误");error.printStackTrace();}public static synchronized int getOnlineCount() {return onlineCount;}public static synchronized void addOnlineCount() {WebSocket.onlineCount++;}public static synchronized void subOnlineCount() {WebSocket.onlineCount--;}
}
5:对外暴露接口
package com.XX.io.ws;import com.XX.common.base.AjaxResult;
import com.XX.common.base.BaseController;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import net.sf.json.JsonConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** 类描述:** @author :carry* @version: 1.0 CreatedDate in 2019年09月24日* <p>* 修订历史: 日期 修订者 修订描述*/
@RestController
@RequestMapping("/websocket")
public class SocketController extends BaseController {@Autowiredprivate WebSocket webSocket;/** * @方法描述: 向所有用户发送消息(一人对所有人发布同一个消息)* @return: com.XX.common.base.AjaxResult* @Author: carry*/@PostMapping("/sendAllWebSocket")public AjaxResult sendAllWebSocket(@RequestParam String jsonMsg) {try {webSocket.sendAllMessage(jsonMsg);} catch (Exception e) {return error(e.getMessage());}return success();}/** * @方法描述: 一对一发送消息(一人对一人发布同一个消息)* @return: com.XX.common.base.AjaxResult* @Author: carry*/@PostMapping("/sendOneWebSocketOneToOne")public AjaxResult sendOneWebSocketOneToOne(@RequestParam("userId") String userId, @RequestParam String jsonMsg) {try {webSocket.sendOneMessage(userId, jsonMsg);} catch (Exception e) {return error(e.getMessage());}return success();}/*** @方法描述: 一对一发送多消息(一人对一人发布多个消息)* 此方法会出现多线程问题,需要在sendOneMessage进行处理* @return: com.XX.common.base.AjaxResult* @Author: carry*/@PostMapping("/sendManayWebSocketOneToOne")public AjaxResult sendManayWebSocketOneToOne(@RequestParam("userId") String userId, @RequestParam String jsonString) {try {JSONArray jsonArray = JSONArray.fromObject(jsonString);for(int i=0;i<jsonArray.size();i++){JSONObject jsonObject= JSONObject.fromObject(jsonArray.get(i));webSocket.sendOneMessage(userId, jsonObject.toString());}} catch (Exception e) {return error(e.getMessage());}return success();}/** * @方法描述: 一对多发送消息(一人对多人发布同一个消息)* @return: com.fencer.common.base.AjaxResult* @Author: carry*/@PostMapping("/sendUserListWebSocket")public AjaxResult sendUserListWebSocket(@RequestParam List<String> userList, @RequestParam String jsonMsg) {try {for (String userId : userList) {webSocket.sendOneMessage(userId, jsonMsg);}} catch (Exception e) {return error(e.getMessage());}return success();}}
6:入口类也贴上吧
package XXX.XXX;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.client.RestTemplate;@EnableEurekaClient
@EnableHystrix
@MapperScan(basePackages = { "com.XX.XX.mapper" })
@ComponentScan(basePackages = "com.XXX.*")
@SpringBootApplication
public class WebSocketApplication implements CommandLineRunner {public static void main(String[] args) {SpringApplication.run(WebSocketApplication.class, args);}@Overridepublic void run(String... args) throws Exception {System.out.println("######################WebSocket服务启动完成!######################");}@Bean@LoadBalancedpublic RestTemplate restTemplate() {return new RestTemplate();}
}
============================其他服务的调用====================================================
二:其他服务的调用,使用rest+template调用(不会的可以复习一下spring colud 服务之间的调用)
1:为了方便服务里面其他功能的方便,封装单独的调用公共类
SocketService.java
package com.xxx.rcdd.io.ws;import com.xxx.common.base.AjaxResult;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import net.sf.json.JSONArray;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;import java.util.List;import static com.fencer.common.base.AjaxResult.error;
import static com.fencer.common.base.AjaxResult.success;/*** 类描述:** @author :carry* @version: 1.0 CreatedDate in 2019年09月26日* <p>* 修订历史: 日期 修订者 修订描述*/
@Service
public class SocketService {@AutowiredRestTemplate restTemplate;/*** @方法描述: 向全部用户广播消息* @return: void* @Author: carry*/public void sedMsgAll(String jsonMsg) {try {//发送消息Thread thread = new Thread(new Runnable() {@Overridepublic void run() {SocketService.this.sendAllWebSocket(jsonMsg);}});thread.start();} catch (Exception e) {throw new RuntimeException(e.getMessage());}}/*** @方法描述:一对一发送消息(一人对一人发布同一个消息)* @return: void* @Author: carry*/public void sendOneMsgToOneToOne(String userId, String jsonMsg) {try {//发送消息Thread thread = new Thread(new Runnable() {@Overridepublic void run() {SocketService.this.sendOneWebSocketOneToOne(userId, jsonMsg);}});thread.start();} catch (Exception e) {throw new RuntimeException(e.getMessage());}}/*** @方法描述:一对一发送多消息(一人对一人发布多个消息)* @return: void* @Author: carry*/public void sendManayToOneToOne(String userId,JSONArray jsonArray) {try {//发送消息Thread thread = new Thread(new Runnable() {@Overridepublic void run() {SocketService.this.sendManayWebSocketOneToOne(userId, jsonArray);}});thread.start();} catch (Exception e) {throw new RuntimeException(e.getMessage());}}/*** @方法描述:一对多发送消息(一人对多人发布同一个消息)* @return: void* @Author: carry*/public void sendUserList(List<String> userList, String jsonMsg) {try {//发送消息Thread thread = new Thread(new Runnable() {@Overridepublic void run() {SocketService.this.sendUserListWebSocket(userList, jsonMsg);}});thread.start();} catch (Exception e) {throw new RuntimeException(e.getMessage());}}/*** @方法描述: 消息发送失败回调* @return: com.fencer.rcdd.domain.util.SysUser* @Author: carry*/private AjaxResult fallbackOfMessage() {System.out.println("error fallbackOfMessage.... ");return error();}/*** @方法描述: 向全部用户广播消息* @return: com.fencer.rcdd.domain.util.SysUser* @Author: carry*/@HystrixCommand(fallbackMethod = "fallbackOfMessage")private AjaxResult sendAllWebSocket(String jsonMsg) {try {String url = "http://API-WEBSOCKET/websocket/sendAllWebSocket";MultiValueMap<String, Object> map = new LinkedMultiValueMap<String, Object>();map.add("jsonMsg", jsonMsg);HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<MultiValueMap<String, Object>>(map, headers);restTemplate.postForEntity(url, request, AjaxResult.class);} catch (Exception e) {e.printStackTrace();return error(e.getMessage());}return success();}/*** @方法描述:一对一发送消息(一人对一人发布同一个消息)* @return: com.fencer.rcdd.domain.util.SysUser* @Author: carry*/@HystrixCommand(fallbackMethod = "fallbackOfMessage")private AjaxResult sendOneWebSocketOneToOne(String userId, String jsonMsg) {try {String url = "http://API-WEBSOCKET/websocket/sendOneWebSocketOneToOne";MultiValueMap<String, Object> map = new LinkedMultiValueMap<String, Object>();map.add("userId", userId);map.add("jsonMsg", jsonMsg);HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<MultiValueMap<String, Object>>(map, headers);restTemplate.postForEntity(url, request, AjaxResult.class);} catch (Exception e) {e.printStackTrace();return error(e.getMessage());}return success();}/*** @方法描述:一对一发送多消息(一人对一人发布多个消息)* @return: com.fencer.rcdd.domain.util.SysUser* @Author: carry*/@HystrixCommand(fallbackMethod = "fallbackOfMessage")private AjaxResult sendManayWebSocketOneToOne(String userId,JSONArray jsonArray) {try {String jsonString = jsonArray.toString();String url = "http://API-WEBSOCKET/websocket/sendManayWebSocketOneToOne";MultiValueMap<String, Object> map = new LinkedMultiValueMap<String, Object>();map.add("userId", userId);map.add("jsonString", jsonString);HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<MultiValueMap<String, Object>>(map, headers);restTemplate.postForEntity(url, request, AjaxResult.class);} catch (Exception e) {e.printStackTrace();return error(e.getMessage());}return success();}/*** @方法描述:一对多发送消息(一人对多人发布同一个消息)* @return: com.fencer.rcdd.domain.util.SysUser* @Author: carry*/@HystrixCommand(fallbackMethod = "fallbackOfMessage")private AjaxResult sendUserListWebSocket(List<String> userList, String jsonMsg) {try {String url = "http://API-WEBSOCKET/websocket/sendUserListWebSocket";MultiValueMap<String, Object> map = new LinkedMultiValueMap<String, Object>();map.add("userList", userList);map.add("jsonMsg", jsonMsg);HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<MultiValueMap<String, Object>>(map, headers);restTemplate.postForEntity(url, request, AjaxResult.class);} catch (Exception e) {e.printStackTrace();return error(e.getMessage());}return success();}
}
2:具体业务的调用
省略业务代码List<Map<String, Object>> infoList = new ArrayList<Map<String, Object>>();Map<String, Object> msgMap = new HashMap<String, Object>();msgMap.put("id", uuid);msgMap.put("type", "shift");msgMap.put("title", "通知内容");infoList.add(msgMap);infoList.add(msgMap);JSONArray jsonArray =JSONArray.fromObject(infoList);JSONObject jsonObject=JSONObject.fromObject(msgMap);//发送消息socketService.sedMsgAll(jsonObject.toString());//socketService.sendOneMsgToOneToOne("3001",jsonObject.toString());// socketService.sendManayToOneToOne("3001", jsonArray);
=================================前端VUE部分====================================================
因为推送可能是在不同的页面,本人是在不变的页面比如导航组件里链接的socket服务进行监听,然后调用element的notic组件进行前台展示,前台可以根据消息的类型或者id,进行自己的处理,比如进入到业务的具体页面之类。
mounted() {//链接socketthis.connWebSocket();},beforeDestroy() {// 监听窗口关闭事件,vue生命周期销毁之前关闭socket当窗口关闭时,防止连接还没断开就关闭窗口。this.onbeforeunload();},methods: {connWebSocket() {let userInfo = JSON.parse(localStorage.getItem("userInfos"));let userId = userInfo.userId;// WebSocketif ("WebSocket" in window) {this.websocket = new WebSocket("ws://localhost:9002/websocket/" + userId //userId 传此id主要后端java用来保存session信息,用于给特定的人发送消息,广播类消息可以不用此参数);//初始化socketthis.initWebSocket();} else {ctx.$message.error("次浏览器不支持websocket");}},initWebSocket() {// 连接错误this.websocket.onerror = this.setErrorMessage;// 连接成功this.websocket.onopen = this.setOnopenMessage;// 收到消息的回调this.websocket.onmessage = this.setOnmessageMessage;// 连接关闭的回调this.websocket.onclose = this.setOncloseMessage;// 监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。window.onbeforeunload = this.onbeforeunload;},setErrorMessage() {console.log("WebSocket连接发生错误 状态码:" + this.websocket.readyState);},setOnopenMessage() {console.log("WebSocket连接成功 状态码:" + this.websocket.readyState);},setOnmessageMessage(result) {console.log("服务端返回:" + result.data);let msgMap = JSON.parse(result.data);let id = msgMap.id;let title = msgMap.title;let type = msgMap.type;// 根据服务器推送的消息做自己的业务处理this.$notify({title: "你有一条新信息",type: "info",duration: 0,dangerouslyUseHTMLString: true,message:'<div style="height:100px;width:100px">' +title,position: "bottom-right"});},setOncloseMessage() {console.log("WebSocket连接关闭 状态码:" + this.websocket.readyState);},onbeforeunload() {this.closeWebSocket();},closeWebSocket() {this.websocket.close();}},
基本上面的代码复制到你们自己的工程里就能运行,有些涉及数据库、地址、或者公司隐私之类的,大部分用XXX去掉了,换成你们自己的即可。
======================================正式环境配合nginx使用=======================================
因为服务器对外只开通了8077端口,所以只能通过Nginx来反向代理
1:修改前台文件
connWebSocket() {let userInfo = JSON.parse(localStorage.getItem("userInfos"));let userId = userInfo.userId;if ("WebSocket" in window) {this.websocket = new WebSocket("ws://xxx.xxx.xxx.xxx:8077/websocket/" + userId);this.initWebSocket();} else {ctx.$message.error("次浏览器不支持websocket");}},
2:修改Nginx配置文件
主要的2点
1: map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
2: websocket 服务的端口是9002,需要代理到此端口
location /websocket {
proxy_pass http://127.0.0.1:9002;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 3600s; #超时时间
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade; #开启websocket支持
proxy_set_header Connection $connection_upgrade; #开启websocket支持
}
完整代码:
#user nobody;
worker_processes 1;#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;#pid logs/nginx.pid;events {worker_connections 65555;
}http {map $http_upgrade $connection_upgrade {default upgrade;'' close;}include mime.types;default_type application/octet-stream;#log_format main '$remote_addr - $remote_user [$time_local] "$request" '# '$status $body_bytes_sent "$http_referer" '# '"$http_user_agent" "$http_x_forwarded_for"';#access_log logs/access.log main;sendfile on;autoindex on;tcp_nopush on;tcp_nodelay on; keepalive_timeout 120; fastcgi_connect_timeout 300;fastcgi_send_timeout 300;fastcgi_read_timeout 300;fastcgi_buffer_size 64k;fastcgi_buffers 4 64k; fastcgi_busy_buffers_size 128k;fastcgi_temp_file_write_size 128k;client_header_buffer_size 100m; #上传文件大小限制 gzip on;gzip_min_length 1k;gzip_buffers 4 16k;gzip_http_version 1.0;gzip_comp_level 9;gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/javascript image/jpeg image/gif image/png;#gzip_types text/plain application/x-javascript text/css application/xml;gzip_vary on;server {listen 8077;server_name localhost;#charset koi8-r;#access_log logs/host.access.log main;location / {root D:/dist; index index.html index.htm;}location /iserver {proxy_pass http://127.0.0.1:8090/iserver;}location ^~/api {proxy_pass http://127.0.0.1:8088;}location /websocket {proxy_pass http://127.0.0.1:9002; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_read_timeout 3600s; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; }error_page 404 /index.html;location /index.html {root D:/dist; index index.html index.htm;}}
==============================添加 消息提示音====================================================
1:vue页面修改,添加代码
<!-- 语音播放功能 --><audio ref="audio" controls v-show="false"><source id="tts_source_id" src="../assets/mp3/messageToYou.mp3" type="audio/mpeg"></audio>
2:添加一个调用的方法
methods: {myplay(){this.$refs.audio.play();},
}
3:推送完消息后调用
setOnmessageMessage(result) {this.myplay();//调用播放方法console.log("服务端返回:" + result.data);let msgMap = JSON.parse(result.data);let id = msgMap.id;let business_Id = msgMap.business_Id;let title = msgMap.title;let type = msgMap.type; //主要
.........省略其他代码
}
Spring clound+VUE+WebSocket实现消息推送 一(即时通讯)相关推荐
- 消息推送和即时通讯项目简单实现
使用Netty实现的服务端消息推送,Android端消息接收功能 各子项目功能说明如下: 1.IMClient:Android客户端工程 2.IMServer:服务端Web工程,负责监控连接和推送消息 ...
- springboot定时发送短信_springboot 整合websocket实现消息推送(主动推送,具体用户推送,群发,定时推送)...
websocket springboot 整合websocket实现消息推送(主动推送,具体用户推送,群发,定时推送) 使用WebSocket构建交互式Web应用程序 本指南将引导您完成创建" ...
- Vue-全局websocket 实现消息推送
在上一篇文章 WebSocket 消息推送https://blog.csdn.net/qq_63312957/article/details/125375122?spm=1001.2014.3001. ...
- vue-admin websocket接收消息推送+语音提示(详细代码)
websocket接收消息推送+语音提示 这个是同事的代码,我拿来记录一下,希望以后可以看得懂-- utils/websocket.js const audioUrl = require('@/ass ...
- php通知websocket,php实现websocket实时消息推送
php实现websocket实时消息推送,供大家参考,具体内容如下 SocketService.php /** * Created by xwx * Date: 2017/10/18 * Time: ...
- python websocket实现消息推送_python Django websocket 实时消息推送
[实例简介] Django websocket 实时消息推送 服务端主动推送 调用 send(username, title, data, url) username:用户名 title:消息标题 d ...
- python websocket实时消息推送
python websocket实时消息推送 十分想念顺店杂可... 本人写的渣,大神勿喷. 转载请附带本文链接,谢谢. 服务端代码 # -*- coding: utf-8 -*- # @Time : ...
- java整合消息推送_SpringMVC整合websocket实现消息推送及触发功能
本文为大家分享了SpringMVC整合websocket实现消息推送,供大家参考,具体内容如下 1.创建websocket握手协议的后台 (1)HandShake的实现类 /** *Project N ...
- spring boot 集成socketIo 做消息推送
spring boot 集成socketIo 做消息推送 项目需求 代码展示 客户端代码 服务端代码 项目需求 后台管理系统用户小铃铛,消息推送功能并展示有多少条消息或者小红点 代码展示 客户端代码 ...
最新文章
- Mac下显示隐藏文件
- SOL注入——HTTP头部注入(六)
- 【音视频安卓开发 (三)】OpenGL ES 直接绘制YUV
- 从微信浏览器,调起本地应用,最简单的解决方案
- php mysql rpm包_MYSQL RPM包安装
- 定义域可以写成不等式吗_证单变量不等式(一)
- mysql union 不同字段_mysql中union和union all的区别和注意点
- ssm集成mysql_idea+SSM+Mysql框架整合
- ios中UIWebview和asiHttprequest的用法
- office如何快速批量地压缩PPT内的全部图片
- 电脑操作技巧:如何抓图
- zbox的测试例——selectAll+selectInverse
- Linux网卡up但是没有running,eth0 up但是没有running的小问题
- Delphi文件正文提取开发组件--文件内容搜索的高效工具
- 正则表达式中的方法、检测、特殊字符、敏感词替换等等
- NASA丨登陆火星六大前沿技术
- 通过DataEase行列权限设置实现数据权限管控
- matlab handles结构体
- 新世纪福音战士剧场版破
- 极品家丁最新章节列表
热门文章
- 视频教程-Linux shell脚本习题-Linux
- 贪心算法(局部最优)
- 关于ASO与ASM的结合
- 解锁一个新技能,如何在Python代码中使用表情包...
- 人脑是怎样认知图像的?——结构描述模式(传统模式识别之五)
- 马蜂窝推荐系统容灾缓存服务的设计与实现
- 在 Target 中获取项目引用的所有依赖(dll/NuGet/Project)的路径
- 2021年12月国产数据库排行榜: openGauss节节攀升拿下榜眼,GaussDB与TDSQL你争我夺各进一位...
- SQL Based on practice
- 计蒜客——Nise-Anti-AK Problem