本篇博客只是记录websocket在自己的项目中的应用,只是记录,不做说明(后来替换为GoEasy了)。

/**
* 握手的设置,这其实是为了获取session
*/
public class GetHttpSessionConfigurator extends ServerEndpointConfig.Configurator {
@Override
public void modifyHandshake(ServerEndpointConfig config,
HandshakeRequest request,
HandshakeResponse response) {
HttpSession httpSession = (HttpSession) request.getHttpSession();
config.getUserProperties().put(HttpSession.class.getName(), httpSession);
}
}
/**
* JSR356定义了WebSocket的规范,Tomcat7中实现了该标准。
* JSR356 的 WebSocket 规范使用 javax.websocket.*的 API,
* 可以将一个普通 Java 对象(POJO)使用 @ServerEndpoint 注释作为 WebSocket 服务器的端点。
* value必须以"/"开始,是否已"/"结束无所谓,
* configurator指的是ServerEndpointConfig 的配置信息,可以配置握手协议等信息,而在本例中是为了获取session
*/
@ServerEndpoint(value = "/websocket/task", configurator = GetHttpSessionConfigurator.class)
public class TaskServer { private static Logger logger = LoggerFactory.getLogger(TaskServer.class); //ConcurrentHashMap在线程安全的基础上提供了更好的写并发能力,但同时降低了对读一致性的要求
private static final Map<String, Session> doctorMap = new ConcurrentHashMap<String, Session>(); //使用ServerEndpoint注释的类必须有一个公共的无参数构造函数
public TaskServer() {
} @OnOpen
public void onOpen(Session session, EndpointConfig config) {
HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
if (httpSession == null) {
return;
}
// DoctorUser doctor = (DoctorUser) httpSession.getAttribute(Constants.SESSION_DOCTOR);
Doctoruser doctor = (Doctoruser) httpSession.getAttribute(Constants.SESSION_DOCTOR_USER); String doctorID = doctor.getId();
logger.info("doctor " + doctor.getName() + " id = " + doctor.getId() + " 建立连接");
doctorMap.put(doctorID, session);
// attributes.put(Constants.WEBSOCKET_DOCTORNAME,userName);
} @OnClose
public void onClose(Session session) {
// doctorMap.remove( doctorID);
boolean has = doctorMap.containsValue(session);
String doctorId = null;
if (has == true) {
for (Map.Entry entry : doctorMap.entrySet()) {
if (entry.getValue().equals(session)) {
doctorId = (String) entry.getKey();
doctorMap.remove(doctorId);
}
}
} else {
logger.info("on close error");
}
logger.info("doctor " + doctorId + " 断开连接");
} @OnMessage
public void incoming(String message, Session session) {
//logger.info("recv message: " + message);
sendMessage("pong",session);
} @OnError
public void onError(Throwable t) throws Throwable {
logger.info("socket 连接错误 " + t.toString());
} //把消息推送给前端,在本例中推送是给doctorID推送message(数据库表中任务表的id)
public static void broadcast(String doctorID, String message) {
Session session = doctorMap.get(doctorID);
if (session == null) {
logger.warn("用户:" + doctorID + "socket连接不存在");
return;
} session.getAsyncRemote().sendText(message); //非阻塞、异步 /*try {
//详见 《 websocket getAsyncRemote()和getBasicRemote()区别》
//http://blog.csdn.net/who_is_xiaoming/article/details/53287691 //session.getBasicRemote().sendText(message); //阻塞、同步
session.getAsyncRemote().sendText(message); //非阻塞、异步
} catch (IOException e) {
logger.error("推送消息错误--" + e);
doctorMap.remove(doctorID);
try {
session.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}*/
} /**
* 发送信息
*
* @param message 发送内容
* @param session 用户session
*/
public void sendMessage(String message, Session session) {
try {
synchronized (session) {
if (session.isOpen()) {
session.getBasicRemote().sendText(message);
}
}
} catch (Exception e) {
logger.error("send message exception", e);
}
} public static boolean hasConnection(String doctorID) {
if (doctorMap.get(doctorID) == null) return false;
return true;
} public static List<String> getDoctorList() {
ArrayList<String> doctors = new ArrayList<>();
for (Map.Entry entry : doctorMap.entrySet()) {
doctors.add((String) entry.getKey());
}
return doctors;
} public static Map<String, Session> getDoctorMap() {
return doctorMap;
}
}
<script type="text/javascript">
/*
1.连接上之后,每秒发送一个心跳,服务器同样返回一个心跳,用来表示服务器没挂。
2.断线重连(我们测试的环境是断开网络连接),断开网络后,心跳包无法发送出去,所以如果当前时间距离上次成功心跳的时间超过20秒,说明连接已经出现问题了,此时需要关闭连接。
3.第一次关闭连接时websocket会尝试重连,设置了一个时间期限,10秒。10秒内如果能连上(恢复网络连接)就可以继续收发消息,连不上就关闭了,并且不会重连。
4.30秒内收不到服务器消息(心跳每秒发送),我就认为服务器已经挂了,就会调用close事件,然后进入第3步。
*/
/*
•一个定时器ws.keepAliveTimer,用来每秒发送一次心跳。
•上次心跳成功的时间ws.last_health_time以及当前时间let time = new Date().getTime();。
•断开连接(ws.close())时的时间reconnect,因为在close事件发生后需要重连10秒。
•是否已经重连过reconnectMark。
•断开连接(ws.close())时需要保存ws对象tempWs。我曾试图ws = { ...ws }发现会丢失绑定的事件。
•一个定时时间为30秒的setTimeout定时器ws.receiveMessageTimer,用来表示服务器是否在30秒内返回了消息。
*/ var ws = null;
var url = 'ws://' + window.location.host + "${rootPath}/websocket/task"; var queue = new Queue(); //js的队列先进先出,把推送任务放到里面 $(function () {
connect(url);
getTask();
}); //==============================================推送================================================
function connect(url) {
// 用户登录了并且没有连接过websocket
if ("${sessionScope.SESSION_DOCTOR_ACCOUNT.id}".length == 32) {
ws = new WebSocket(url);
ws.last_health_time = -1; // 上次心跳成功的时间 //保持连接
ws.keepalive = function () {
var time = new Date().getTime(); //当前时间
/*断线重连(我们测试的环境是断开网络连接),断开网络后,心跳包无法发送出去,
所以如果当前时间距离上次成功心跳的时间超过20秒,说明连接已经出现问题了,此时需要关闭连接。*/
if (ws.last_health_time !== -1 && time - ws.last_health_time > 20000) {
ws.close();
console.log("Info: connection closed.");
} else {
/*连接上之后,每秒发送一个心跳,服务器同样返回一个心跳,用来表示服务器没挂。
ws.bufferedAmount === 0 && ws.readyState === 1 均表示连接是正常的*/
if (ws.bufferedAmount === 0 && ws.readyState === 1) {
ws.send("ping");
ws.last_health_time = time;
}
}
} if (ws) {
var reconnectTime = 0; //断开连接的时间
var reconnectMark = false; //是否重连过
ws.onopen = function () {
reconnectTime = 0;
reconnectMark = false;
// 30s没收到信息,代表服务器出问题了,关闭连接。如果收到消息了,重置该定时器。
ws.receiveMessageTimer = setTimeout(function () {
ws.close();
console.log("Info: connection closed.");
}, 30000);
// 为1表示连接处于open状态,keepAliveTimer用来每秒发送一次心跳;
if (ws.readyState === 1) {
ws.keepAliveTimer = setInterval(function () {
ws.keepalive();
}, 5000);
}
console.log("Info: connection opened. " + url);
} ws.onerror = function () {
console.error("onerror");
}
ws.onmessage = function (event) {
//console.log("Received:" + event.data+" "+new Date().getTime());
//真正的逻辑代码
if(event.data!="pong"){
queue.push(event.data);
playSound();
$("#taskNum").html(queue.size());
var src1 = $("#iframe").attr("src");
if (src1 == "doctor/noTask.do") {
srcValue("doctor/ecgTask.do");
}
} // 收到消息,重置定时器
clearTimeout(ws.receiveMessageTimer);
// 30s没收到信息,代表服务器出问题了,关闭连接。
ws.receiveMessageTimer = setTimeout(function () {
ws.close();
console.log("Info: connection closed.");
}, 30000);
} ws.onclose = function () {
clearTimeout(ws.receiveMessageTimer);
clearInterval(ws.keepAliveTimer);
// 如果没有重连过,进行重连。
if (!reconnectMark) {
reconnectTime = new Date().getTime();
reconnectMark = true;
}
var tempWs = ws; // 保存ws对象 /*第一次关闭连接时websocket会尝试重连,设置了一个时间期限,10秒。
10秒内如果能连上(恢复网络连接)就可以继续收发消息,连不上就关闭了,并且不会重连。*/
if (new Date().getTime() - reconnectTime >= 10000) {
ws.close();
console.log("Info: connection closed.");
} else {
ws = new WebSocket(url);
ws.onopen = tempWs.onopen;
ws.onmessage = tempWs.onmessage;
ws.onerror = tempWs.onerror;
ws.onclose = tempWs.onclose;
ws.keepalive = tempWs.keepalive;
ws.last_health_time = -1;
}
}
} else {
alert("This browser does not support webSocket");
return false;
}
}
} <!--断开连接-->
function disconnect() {
if (ws!= null) {
ws.close();
ws = null;
window.location = document.getElementsByTagName("base")[0].getAttribute("href")+ "doctor/logout.do";
}
} <!--播放音乐-->
function playSound() {
var borswer = window.navigator.userAgent.toLowerCase();
if (borswer.indexOf("ie") >= 0) {
//IE内核浏览器
var strEmbed = '<embed name="embedPlay" src="sounds/doctorTask.wav" autostart="true" hidden="true" loop="false"></embed>';
if ($("body").find("embed").length <= 0)
$("body").append(strEmbed);
var embed = document.embedPlay;
//浏览器不支持 audion,则使用 embed 播放
embed.volume = 100;
//embed.play();这个不需要
} else {
var audio = new Audio("sounds/doctorTask.wav");
//audio.loop="loop";
audio.play();
}
} <!--onbeforeunload 事件在即将离开当前页面(刷新或关闭)时触发。-->
window.onbeforeunload = signout;
function signout() {
disconnect();
} //改变iframe的src值
function srcValue(val) {
$("#iframe").attr("src", val);
} //退出
function logout(){
disconnect();
$("#exitModal").modal("hide");
}
</script>

WebSocket推送的更多相关文章

  1. 用 Go 编写一个简单的 WebSocket 推送服务

    用 Go 编写一个简单的 WebSocket 推送服务 本文中代码可以在 github.com/alfred-zhong/wserver 获取. 背景 最近拿到需求要在网页上展示报警信息.以往报警信息 ...

  2. Golang websocket推送

    Golang websocket推送 在工作用主要使用的是Java,也做过IM(后端用的netty websocket).最近想通过Golang重写下,于是通过websocket撸了一个聊天室. 项目 ...

  3. 模拟websocket推送消息服务mock工具二

    模拟websocket推送消息服务mock工具二 在上一篇博文中有提到<使用electron开发一个h5的客户端应用创建http服务模拟后端接口mock>使用electron创建一个模拟后 ...

  4. GoEasy实现websocket 推送消息通知到客户端

    最近在实现一个推送功能,用户扫描二维码签到,后台及时将签到成功信息推送到浏览器端.排除了前端ajax轮询的方式,决定采用websocket及时推送. 于是发现了第三方websocket推送库GoEas ...

  5. 小谢第37问:关于websocket推送进度,本地保存进度条,然后跳出页面进入后再显示的问题

    1.主要技术点:sessionStorage 会话存储进度 这里在使用之前,顺便说一下cookie.sessionStorage.localStorage 共同点:都是保存在浏览器端,且同源的. 区别 ...

  6. 利用奇偶数来获取websocket推送时间间隔(或者比较前一个数和下一个数的变化)

    利用奇偶数来获取websocket推送时间间隔(或者比较前一个数和下一个数的变化) 在vue中的 data () {     return { countTime: 0,         newDat ...

  7. 基于Java的WebSocket推送

    WebSocket的主动推送 关于消息推送,现在的解决方案如轮询.长连接或者短连接,当然还有其他的一些技术框架,有的是客户端直接去服务端拿数据. 其实推送推送主要讲的是一个推的概念,WebSocket ...

  8. web全栈应用【爬取(scrapy)数据 -> 通过restful接口存入数据库 -> websocket推送展示到前台】

    作为 https://github.com/fanqingsong/web_full_stack_application 子项目的一功能的核心部分,使用scrapy抓取数据,解析完的数据,使用 pyt ...

  9. 从构建分布式秒杀系统聊聊WebSocket推送通知

    秒杀架构到后期,我们采用了消息队列的形式实现抢购逻辑,那么之前抛出过这样一个问题:消息队列异步处理完每个用户请求后,如何通知给相应用户秒杀成功? 场景映射 首先,我们举一个生活中比较常见的例子:我们去 ...

随机推荐

  1. Spring的IOC注解开发入门2

    注解方式设置属性的值 在我们IOC基于xml属性注入的方式中有(一般推荐set方法) 构造方法注入普通值:<constructor-arg>的使用 set方法注入普通值:<prope ...

  2. 微信H5开发,页面被缓存,不更新

    原文:https://blog.csdn.net/qq_27471405/article/details/79295348  这里只是备份 前言:每一次请求,我们都知道浏览器会做一定处理,其中就包括对 ...

  3. ES5-ES6-ES7_字符串扩展—模板字符串

    includes(), startsWith(), endsWith() 传统上,JavaScript只有indexOf方法,可以用来确定一个字符串是否包含在另一个字符串中.ES6又提供了三种新方法 ...

  4. 这些Linux命令,让你的工作事半功倍!

    这些Linux命令,让你的工作事半功倍! 最近都在和Linux打交道,感觉还不错.我觉得Linux相比windows比较麻烦的就是很多东西都要用命令来控制,当然,这也是很多人喜欢linux的原因,比较 ...

  5. shzr要填的各种坑

    shzr要填的各种坑 如果大家看到我学了什么算法没写总结,或者做了什么题没写题解,欢迎让我填坑. 计划要写的: [ ] 点分治 [ ] 整体二分 [ ] CDQ分治 [ ] Min-Max容斥 [√] ...

  6. CSAPP:信息的表和处理1

    CSAPP:信息的表和处理1 关键点:寻址.内存.磁盘.虚拟地址.物理地址.整型数组. 信息存储中的几个概念整型数据类型无符号数有符号数几个概念有符号数与无符号数之间转换基于栈与基于寄存器的区别 信息 ...

  7. 深入理解session机制

    原文链接https://blog.csdn.net/xihuangwutong/article/details/9819033 1. session概念 2. http协议与状态保持 3. 理解coo ...

  8. 【ES6】export和important使用区别

    export命令 export { name1, name2, …, nameN }; export { variable1 as name1, variable2 as name2, …, name ...

  9. 在使用 Spring Boot 和 MyBatis 动态切换数据源时遇到的问题以及解决方法

    相关项目地址:https://github.com/helloworlde/SpringBoot-DynamicDataSource 1. org.apache.ibatis.binding.Bind ...

  10. OmniPlan 3 Pro密钥

    密钥用户名都是youliyuan.OmniPlan 3:HOMJ-QOJH-OIBN-TNIH-HWUN-TEEH-WUNNKWO-HVKB-JAZE-UIHH-XAVY-BEEX-AVYBCRW-M ...