zoukankan      html  css  js  c++  java
  • 基于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<WebSocketEntity> connections = new CopyOnWriteArraySet<WebSocketEntity>();
        
        /**
         * 
         * @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<String>  
         * @throws
         * @author 黑暗料理界扛把子
         * @date 2018年5月10日
         */
        @SuppressWarnings("unused")
        private synchronized Set<WebSocketEntity> 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);
        }
    }
  • 相关阅读:
    html 上传图片前预览
    php获取当月天数及当月第一天及最后一天、上月第一天及最后一天实现方法
    php 计算 pdf文件页数
    php 获取半年内每个月的订单数量, 总价, 月份
    php 获取两个数组之间不同的值
    小程序支付功能
    关于nginx的Job for nginx.service failed because the control process exited with error code.错误
    linux 安装 Apollo
    MongoDB待续。。。
    ABP vNext...待续
  • 原文地址:https://www.cnblogs.com/xiejn/p/15553232.html
Copyright © 2011-2022 走看看