java web 心跳机制实现,基于javax的websocket服务端实现,含心跳机制
websocket连接类
package com.dnn.controller.inter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import javax.websocket.CloseReason;
import javax.websocket.CloseReason.CloseCodes;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import org.apache.http.impl.entity.EntitySerializer;
import com.dnn.entity.WebSocketEntity;
import com.dnn.model.TbAdminMember;
import com.dnn.service.TbAdminMemberService;
import com.dnn.service.TbDataGrouprecordService;
import com.dnn.utils.jfinal.BaseController;
import com.jfinal.aop.Clear;
import net.sf.json.JSONObject;
@Clear
@ServerEndpoint("/webSocket/{userId}")
public class WebSocketController extends BaseController {
protected TbDataGrouprecordService tbDataGrouprecordService=new TbDataGrouprecordService();
protected TbAdminMemberService tbAdminMemberService=new TbAdminMemberService();
private static boolean isHeart=false;
private static final Set connections = new CopyOnWriteArraySet();
/**
*
* @Description: 连接方法
* @param @param userId
* @param @param session
* @return void
* @throws IOException
* @throws
* @author 黑暗料理界扛把子
* @date 2018年5月10日
*/
@OnOpen
public synchronized void onOpen(@PathParam("userId") String userId, Session session) throws IOException {
TbAdminMember member=tbAdminMemberService.findInfo(new TbAdminMember().setId(userId));
if(null==member){
logger.debug("发现未知生物");
return;
}
addUser(member, session);
if(connections.size()==1 && !isHeart){
isHeart=true;
startHeart();
}
}
/**
*
* @Description: 收到消息执行
* @param @param userId
* @param @param message
* @param @param session
* @param @throws IOException
* @return void
* @throws
* @author 黑暗料理界扛把子
* @date 2018年5月10日
*/
@OnMessage
public synchronized void onMessage(@PathParam("userId") String userId, String message, Session session) throws IOException {
logger.info(message);
JSONObject jsonObject = JSONObject.fromObject(message);
if(jsonObject.has("secret") && jsonObject.getString("secret").equals("ping")){//心跳
logger.info("收到"+userId+"的心跳"+message);
//如果收到了心跳 这里设置isHeart为true
WebSocketEntity entity=getUserEntity(userId);
if(null!=entity){
entity.setHeart(true);
}
}else{//普通对话
boolean res=tbDataGrouprecordService.addGroupRecord(jsonObject);
logger.warn("保存记录:"+res);
SimpleDateFormat sdfTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date();
String datetime = sdfTime.format(date);//获取当前时间
jsonObject.put("intime", datetime);
message=tbAdminMemberService.addUserInfo(jsonObject);
sendMsg(message);
}
}
/**
*
* @Description: 链接错误执行
* @param @param userId
* @param @param session
* @param @param error
* @return void
* @throws IOException
* @throws
* @author 黑暗料理界扛把子
* @date 2018年5月10日
*/
@OnError
public synchronized void onError(@PathParam("userId") String userId, Session session, Throwable error) throws IOException {
logger.debug(userId+":发生了错误");
removeUser(userId,new CloseReason(CloseCodes.NO_EXTENSION, "客户端异常"));
error.printStackTrace();
}
@OnClose
public synchronized void onClose(@PathParam("userId") String userId,Session session,CloseReason reason){
logger.debug(userId+":退出了链接");
removeUser(userId,reason);
}
/**
*
* @Description: 获取在线人数
* @param @return
* @return int
* @throws
* @author 黑暗料理界扛把子
* @date 2018年5月10日
*/
private synchronized int getUserOnlineNum(){
return connections.size();
}
/**
*
* @Description: 获取在线人数列表
* @param @return
* @return Set
* @throws
* @author 黑暗料理界扛把子
* @date 2018年5月10日
*/
@SuppressWarnings("unused")
private synchronized Set getUserOnline(){
return connections;
}
/**
*
* @Description: 用户上线
* @param @param member
* @param @param session
* @return void
* @throws
* @author 黑暗料理界扛把子
* @date 2018年5月10日
*/
private synchronized void addUser(TbAdminMember member, Session session){
WebSocketEntity entity=getUserEntity(member.getId());
if(null==entity){
connections.add(new WebSocketEntity(member, session));
}else{
entity.setSession(session);
entity.setMemberHead(member.getHead());
entity.setMemberName(member.getName());
logger.debug("用户"+entity.getMemberName()+"上线了,当前人数为:"+getUserOnlineNum());
}
}
/**
*
* @Description: 根据userId获取实体类
* @param @param userId
* @param @return
* @return WebSocketEntity
* @throws
* @author 黑暗料理界扛把子
* @date 2018年5月22日
*/
private static WebSocketEntity getUserEntity(String userId){
WebSocketEntity entity=null;
if(connections.size()==0)
return entity;
for (WebSocketEntity webSocketEntity : connections) {
if(webSocketEntity.getUserId().contentEquals(userId)){
entity=webSocketEntity;
break;
}
}
return entity;
}
/**
*
* @Description: 用户下线
* @param @param userId
* @param @param reason
* @return void
* @throws
* @author 黑暗料理界扛把子
* @date 2018年5月23日
*/
private void removeUser(String userId, CloseReason reason) {
WebSocketEntity entity=getUserEntity(userId);
if(null!=entity){
try {
if(entity.getSession().isOpen()){
entity.getSession().close(reason);
}
connections.remove(entity);
} catch (IOException e) {
logger.info(e.toString());
e.printStackTrace();
}
}
logger.debug("当前人数:"+connections.size());
}
/**
*
* @param 发送心跳包
* @Description: 服务端群发消息
* @param @param message
* @param @throws IOException
* @return void
* @throws
* @author 黑暗料理界扛把子
* @date 2018年5月10日
*/
public synchronized void sendPing(String message) throws IOException{
if(connections.size()<=0)
return;
for (WebSocketEntity webSocketEntity : connections) {
synchronized (webSocketEntity) {
webSocketEntity.setTimeStr(getTimeInMillis());
webSocketEntity.setHeart(false);
((Session) webSocketEntity.getSession()).getBasicRemote().sendText(message);
}
}
}
/**
*
* @Description: 发消息
* @param @param message
* @param @throws IOException
* @return void
* @throws
* @author 黑暗料理界扛把子
* @date 2018年5月11日
*/
public synchronized void sendMsg(String message) throws IOException{
if(connections.size()<=0)
return;
for (WebSocketEntity entity : connections) {
synchronized (entity) {
((Session) entity.getSession()).getBasicRemote().sendText(message); // 回复用户
}
}
}
/**
*
* @Description: 启动心跳包
* @param
* @return void
* @throws
* @author 黑暗料理界扛把子
* @date 2018年5月10日
*/
private synchronized void startHeart(){
ExamineHeartThread examineHeart =new ExamineHeartThread();
Thread examineThread=new Thread(examineHeart);
KeepHeartThread keepHeart=new KeepHeartThread();
Thread keepThread=new Thread(keepHeart);
keepThread.start();
examineThread.start();
}
/**
*
* @Description: 获取时间戳
* @param @return
* @return long
* @throws
* @author 黑暗料理界扛把子
* @date 2018年5月22日
*/
private static long getTimeInMillis(){
Calendar c = Calendar.getInstance();
c.set(Calendar.SECOND,c.get(Calendar.SECOND)+8);
return c.getTimeInMillis();
}
/**
*
* @author 黑暗料理界扛把子
*
* @Description server发送心跳包 10秒一次
*/
private class KeepHeartThread implements Runnable {
@Override
public void run() {
JSONObject heartJson=new JSONObject();
heartJson.put("type", "0");
heartJson.put("secret", "heart_keep");
while (true) {
try {
logger.debug("发送心跳包当前人数为:"+getUserOnlineNum());
sendPing(heartJson.toString());
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
*
* @author 黑暗料理界扛把子
*
* @Description 检测是否收到client心跳 每秒一次
*/
private class ExamineHeartThread implements Runnable{
@Override
public void run() {
while (true) {
try {
long timeMillins=System.currentTimeMillis();
for (WebSocketEntity entity : connections) {
logger.debug(timeMillins);
logger.info(entity.getTimeStr());
logger.debug(timeMillins>entity.getTimeStr());
if(!entity.isHeart() && entity.getTimeStr()!=0 && timeMillins>entity.getTimeStr()){
logger.debug(entity.getMemberName()+"挂了");
onClose(entity.getUserId(),entity.getSession(),new CloseReason(CloseCodes.NORMAL_CLOSURE, "没有收到心跳"));
}
}
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
WebSocketEntity实体类
package com.dnn.entity;
import javax.websocket.Session;
import com.dnn.model.TbAdminMember;
public class WebSocketEntity {
private String userId;//用户id
private Session session;
private String memberName;//用户姓名
private String memberHead;//头像
private long timeStr;//记录下次发送时间的时间戳
private boolean isHeart=false;//是否收到了心跳
public boolean isHeart() {
return isHeart;
}
public void setHeart(boolean isHeart) {
this.isHeart = isHeart;
}
public String getMemberName() {
return memberName;
}
public void setMemberName(String memberName) {
this.memberName = memberName;
}
public String getMemberHead() {
return memberHead;
}
public void setMemberHead(String memberHead) {
this.memberHead = memberHead;
}
public long getTimeStr() {
return timeStr;
}
public void setTimeStr(long timeStr) {
this.timeStr = timeStr;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public Session getSession() {
return session;
}
public void setSession(Session session) {
this.session = session;
}
public WebSocketEntity(String userId, Session session, String memberName, String memberHead) {
super();
this.userId = userId;
this.session = session;
this.memberName = memberName;
this.memberHead = memberHead;
}
public WebSocketEntity(TbAdminMember member, Session session) {
super();
this.userId = member.getId();
this.session = session;
this.memberName = member.getName();
this.memberHead = member.getHead();
}
@Override
public String toString() {
return "WebSocketEntity [userId=" + userId + ", session=" + session + ", memberName=" + memberName
+ ", memberHead=" + memberHead + ", timeStr=" + timeStr + ", isHeart=" + isHeart + "]";
}
@Override
public int hashCode() {
return this.userId.length();
}
@Override
public boolean equals(Object obj) {
if(!(obj instanceof WebSocketEntity)){
return false;
}
if(obj==this){
return true;
}
return this.userId.equals(((WebSocketEntity)obj).userId);
}
}
java web 心跳机制实现,基于javax的websocket服务端实现,含心跳机制相关推荐
- 基于springboot的websocket服务端和客户端demo(简单易上手)
jdk 1.8 1.导入maven依赖 <!-- websocket服务端依赖 --> <dependency><groupId>org.springframewo ...
- 基于Redisson实现的延时队列RedissonDelayedQueue实现websocket服务端心跳监听
简介 基于Redis的Redisson分布式延迟队列(Delayed Queue)结构的 RDelayedQueue. Java对象在实现了RQueue接口的基础上提供了向队列按要求延迟添加项目的功能 ...
- 基于JAVA融呗智慧金融微资讯移动平台服务端计算机毕业设计源码+数据库+lw文档+系统+部署
基于JAVA融呗智慧金融微资讯移动平台服务端计算机毕业设计源码+数据库+lw文档+系统+部署 基于JAVA融呗智慧金融微资讯移动平台服务端计算机毕业设计源码+数据库+lw文档+系统+部署 本源码技术栈 ...
- 如何设计云存储服务端数据存储加密机制
云存储是一种新型的网络存储形式.随着云存储的广泛使用,云存储中的数据安全问题,如数据泄漏.数据篡改,如何设计云存储服务端数据存储加密机制也成了用户广泛关注的问题.云存储可以分为访问层.应用接口层.基础 ...
- java实现WebSocket服务端
WebSocket服务端 WebSocket服务端配置类 服务端代码 WebSocket服务端配置类 import org.springframework.context.annotation.Bea ...
- [图解教程]Axis2与Eclipse整合开发Web Service之二:WSDL逆向生成服务端
[图解教程]Axis2与Eclipse整合开发Web Service之二:WSDL逆向生成服务端 一般开发Web Service,是选写服务端,再根据生成的WSDL文件生成客户端.看过一本SOA的书中 ...
- 基于java web的图书馆_基于Java-Web的图书管理系统的设计与实现.doc
题 目 基于Java Web的图书管理 系统的设计与实现 指导老师 ****** 专业班级 姓 名 ******* 学 号 ***** 年 月*日 第 PAGE \* MERGEFORMAT 0 页 ...
- java web网上书城_基于Java web的网上书城
源码编号:B-E00009点击查看分类规则 项目类型:Java EE项目(java web项目) 项目名称:基于Java web的网上书城(shinebookshop) 当前版本:V1.0.0版本 难 ...
- java 网上商城系统_基于Java_web开发的网上商城系统(含源文件).doc
PAGE PAGE 1 届 别 学 号 (学校名称) 毕业设计(论文) 基于JAVA WEB的网上商城设计与开 发 姓 名[ 系 别 .专 业 导师姓名.职称[ 完 成 时 间[ PAGE II- 网 ...
最新文章
- Fabric动态增加组织【资料】
- MySQL根被拒绝_[转载]phpMyAdmin 尝试连接到 MySQL 服务器,但服务器拒绝连接。...
- dorado 中的日期--》oracle中的日期
- [vue] 怎么配置使vue2.0+支持TypeScript写法?
- 安卓 camera 调用流程_音视频开发之旅(四)Camera视频采集
- 创建一个简单的数据库
- JSK-115 单独的数字(二)【位运算】
- 怎么清除远程计算机的用户名,Windows8系统如何清除“远程桌面连接”登录历史痕迹...
- C++ vector简单demo
- Windows via C/C++ 学习(6)内核对象
- Android循环ViewPager(二)
- icmp数据包BE、LE解释
- 后缀为axd 的文件
- 火影忍者ol手游服务器注册上限怎么办,火影忍者ol手游进不去是怎么办 火影忍者ol手游上不去原因详解...
- 优秀蓝牙耳机推荐,热销不错的四款蓝牙耳机推荐
- 基于PL/SQL的数据库备份方法
- FastDeRain解读
- 从地图投影折射出的中西方文化差异
- 关于自我的剖析 —— 认知(面试:你的缺点是什么)
- 智能化推送服务MobPush产品简介
热门文章
- Matplotlib绘制简单函数的梯度下降法
- AD5933测量容性负载时的神秘振荡信号
- es mysql 预处理_数据库选型之MySQL vs ElasticSearch
- python获取当前时间的函数_java/python中获取当前系统时间,并与字符串相互转换格式,或者转化成秒数,天数等整数...
- 不正确的c语言语句是,最基本的C语言语句是( )
- python3 学习使用大纲梳理
- c语言文件可用代码存放,C语言 文件(示例代码)
- RUST直接升钢指令_[译]参照TypeScript学习Rust-part-1
- stm32l0的停止模式怎么唤醒_汇聚力量,守护安全:2020 “AnQ唤醒云课堂”圆满收官!...
- ceph-deploy mod add_我的世界基座(Pedestals)Mod