websocket+心跳实现长链接

1.js部分web

var lockReconnect = false;  //避免ws重复链接
		var ws = null;          // 判断当前浏览器是否支持WebSocket
		var wsUrl = "ws:"+ip+":8081";

		createWebSocket(wsUrl);   //链接ws
		
		function createWebSocket(url) {
		    try {
		        if ('WebSocket' in window) {
		            ws = new WebSocket(url);
		        } else if ('MozWebSocket' in window) {
		            ws = new MozWebSocket(url);
		        } else {
		            layui.use(['layer'], function () {
		                var layer = layui.layer;
		                layer.alert("您的浏览器不支持websocket协议,建议使用新版谷歌、火狐等浏览器,请勿使用IE10如下浏览器,360浏览器请使用极速模式,不要使用兼容模式!");
		            });
		        }
		        initEventHandle();
		    } catch (e) {
		        reconnect(url);
		        console.log(e);
		    }
		}
function initEventHandle() {
		    ws.onclose = function () {
		        console.log("llws链接关闭!" + new Date().toUTCString());
		        var start=0;
		        reconnect(wsUrl);
		    };
		    ws.onerror = function () {
		        console.log("llws链接错误!");
		        reconnect(wsUrl);
		    };
		    ws.onopen = function () {
		    	ws.send(uid);
		        heartCheck.reset().start();      //心跳检测重置
		        console.log("llws链接成功!" + new Date().toUTCString());
		    };
		    ws.onmessage = function (event) {    //若是获取到消息,心跳检测重置
		        var eventData = event.data;
		        handMsg(eventData);
		        heartCheck.reset().start();      //拿到任何消息都说明当前链接是正常的
		    };
		}
		
		// 监听窗口关闭事件,当窗口关闭时,主动去关闭websocket链接,防止链接还没断开就关闭窗口,server端会抛异常。
		window.onbeforeunload = function () {
		    ws.close();
		}
		
		function reconnect(url) {
		    if (lockReconnect) return;
		    lockReconnect = true;
		    setTimeout(function () {     //没链接上会一直重连,设置延迟避免请求过多
		        createWebSocket(url);
		        lockReconnect = false;
		    }, 5000);
		}
		
		//心跳检测
		var heartCheck = {
		    timeout: 60000,        //1分钟发一次心跳
		    timeoutObj: null,
		    serverTimeoutObj: null,
		    reset: function () {
		        clearTimeout(this.timeoutObj);
		        clearTimeout(this.serverTimeoutObj);
		        return this;
		    },
		    start: function () {
		        var self = this;
		        this.timeoutObj = setTimeout(function () {
		            ws.send(uid);
		            self.serverTimeoutObj = setTimeout(function () {
		                ws.close();     
		            },self.timeout)
		        },this.timeout)
		    }
		}
function handMsg(eventData){
	//ajax链接后台处理业务...
}

2.后台代码ajax

//使用监听器机制实现ServletContextListener接口
public class WebContextListener implements ServletContextListener{
	
	//消亡时执行的方法
	@Override
	public void contextDestroyed(ServletContextEvent arg0) {
		
	}
	//项目启动时初始化执行的方法
	@Override
	public void contextInitialized(ServletContextEvent arg0) {
		 WebSocketImpl.DEBUG = false;
	        int port = 8081;
	        WsServer wsServer = new WsServer(port);
	        wsServer.start();

	        /*new Thread(new Runnable() {
	            @Override
	            public void run() {
	                while (true) {
	                    try {
	                        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
	                        String msg = simpleDateFormat.format(new Date());
	                        System.out.println(msg);
	                        Collection<String> allOnlineUser = WsPool.getAllOnlineUser();
	                        System.out.println(allOnlineUser.size());
	                        if (allOnlineUser.size() > 0) {
	                            WsPool.sendMessageToAll(msg);
	                        }

	                        Thread.sleep(5000);
	                    } catch (InterruptedException e) {
	                        e.printStackTrace();
	                    }
	                }
	            }
	        }).start();*/
		
	}

}
//链接池
public class WsPool {
	private static final Map<WebSocket, String> wsUserMap = new HashMap<WebSocket, String>();
	//单发
	public static WebSocket test(String name){
        Set<WebSocket> keySet = wsUserMap.keySet();
		for(WebSocket socket : keySet){
			String string = wsUserMap.get(socket);
			if(name.equals(string)){
				return socket;
			}
		}
		return null;
	}
	//群发
	public static List<WebSocket> massTexting(String name){
		List<WebSocket> fbList = new ArrayList<WebSocket>();
			Set<WebSocket> keySet = wsUserMap.keySet();
			for(WebSocket socket : keySet){
				String string = wsUserMap.get(socket);
				if(name.equals(string)){
					fbList.add(socket);
				}
			}
		return fbList;
	}
    /**
     * 经过webSocket链接获取用户
     * @param conn
     * @return
     */
    public static String getUserByWs(WebSocket conn) {
        return wsUserMap.get(conn);
    }

    /**
     * 根据用户获取webSocket对象
     * 此处应返回的是一个websocket集合,可是在close方法中将失效的链接移除,
     * 因此保证返回的是一个
     * @param userName
     * @return
     */
    public static WebSocket getWsByUser(String userName) {
        Set<WebSocket> webSockets = wsUserMap.keySet();
        synchronized (webSockets){
            for (WebSocket conn : webSockets) {
                if (conn.equals(userName)) {
                    return conn;
                }
            }
        }
        return null;
    }

    /**
     * 获取全部的在线用户
     * @return
     */
    public static Collection<String> getAllOnlineUser() {
        List<String> users = new ArrayList<String>();
        Collection<String> values = wsUserMap.values();
        for (String value : values) {
            users.add(value);
        }
        return users;
    }

    /**
     * 向链接池中添加链接
     * @param userName
     * @param conn
     */
    public static void addUser(String userName, WebSocket conn) {
        wsUserMap.put(conn, userName);
    }

    /**
     * 移除池中的特定链接
     * @param conn
     * @return
     */
    public static boolean removeUser(WebSocket conn) {
        if (wsUserMap.containsKey(conn)) {
            wsUserMap.remove(conn);
            return true;
        }else {
            return false;
        }
    }

    /**
     * 向一特定的用户发送消息
     * @param msg
     * @param conn
     */
    public static void sendMessageToUser(String msg, WebSocket conn) {
        if (null != conn && null != wsUserMap.get(conn)) {
            conn.send(msg);
        }
    }

    /**
     * 向所用的用户发送消息
     * @param msg
     */
    public static void sendMessageToAll(String msg) {
        Set<WebSocket> webSockets = wsUserMap.keySet();
        synchronized (webSockets) {
            for (WebSocket conn : webSockets) {
                String user = wsUserMap.get(conn);
                if (null != user) {
                    conn.send(msg);
                }
            }
        }
    }
}
public class WsServer extends WebSocketServer{

    public WsServer(int port) {
        super(new InetSocketAddress(port));
    }

    public WsServer(InetSocketAddress address) {
        super(address);
    }

    /**
     * 链接时触发
     */
    @Override
    public void onOpen(WebSocket webSocket, ClientHandshake clientHandshake) {
        //LOGGER.debug("connected~~~~~~");
        System.out.println("connected~~~~~~");
    }

    /**
     * 断开链接时触发的方法
     */
    @Override
    public void onClose(WebSocket webSocket, int code, String reason, boolean remote) {
        //断开链接时,移除链接
        userLeave(webSocket);
        System.out.println(reason);
    }

    @Override
    public void onMessage(WebSocket webSocket, String msg) {
        	System.out.println(msg);//获取用户名
            userJoin(webSocket, msg);
            webSocket.send(msg);
    }

    /**
     * 错误时触发
     */
    @Override
    public void onError(WebSocket webSocket, Exception e) {
        System.out.println("on error");
        e.printStackTrace();
    }

    /**
     * 将链接从池中移除
     * @param conn
     */
    private void userLeave(WebSocket conn) {
        WsPool.removeUser(conn);
    }

    /**
     * 向池中添加链接
     * @param conn
     * @param userName
     */
    private void userJoin(WebSocket conn, String userName) {
        WsPool.addUser(userName, conn);
    }
}
//在service层处理完业务逻辑以后在控制层调用链接池的方法
WebSocket webSocket = WsPool.test(us.getuId());
		if(webSocket!=null){
			webSocket.send(us.getuId());
		}

本人已测可用!浏览器