基于tomcat 7.0.68 的websocket 实现,及通过 HttpSessionId 实现websocket session 共享

来源:互联网 发布:o2olr软件下载 编辑:程序博客网 时间:2024/05/18 01:10

0、先把页面调用代码贴出来好了

var historyWebsocket = null;//  var websocketOnline = null;      //判断当前浏览器是否支持WebSocket    if('WebSocket' in window){        historyWebsocket = new WebSocket("ws://ip:端口/应用名/historyWebsocket");    }    else{        alert('Not support websocket')    }  //连接发生错误的回调方法    historyWebsocket.onerror = function(){        setMessageInnerHTML("error");    };    //连接成功建立的回调方法    historyWebsocket.onopen = function(event){        send();    } //接收到消息的回调方法    historyWebsocket.onmessage = function(event){        var data = eval("("+event.data+")");//初始化数据    }    //连接关闭的回调方法    historyWebsocket.onclose = function(){        setMessageInnerHTML("close");    }    //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。    window.onbeforeunload = function(){        historyWebsocket.close();    }    //将消息显示在网页上    function setMessageInnerHTML(innerHTML){        $("#message").empty();        document.getElementById('message').innerHTML +='<font color="#00CACA"class="message">' +innerHTML + '</font><br/>';    }    //关闭连接    function closeWebSocket(){        historyWebsocket.close();    }    //发送消息    function send(){//         var message = document.getElementById('text').value;//         mesg="14701661345";//          historyWebsocket.send(message);    }

1、onlineWebsocket.java

import java.io.IOException;import java.util.ArrayList;import java.util.Iterator;import java.util.List;import java.util.Map.Entry;import java.util.Set;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.CopyOnWriteArraySet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpSession;import javax.websocket.EndpointConfig;import javax.websocket.OnClose;import javax.websocket.OnError;import javax.websocket.OnMessage;import javax.websocket.OnOpen;import javax.websocket.Session;import javax.websocket.server.ServerEndpoint;import org.slf4j.Logger;import org.slf4j.LoggerFactory;//该注解用来指定一个URI,客户端可以通过这个URI来连接到WebSocket。类似Servlet的注解mapping。无需在web.xml中配置。@ServerEndpoint(value="/onlineWebsocket",configurator=GetHttpSessionConfigurator.class)public class OnlineStateWebSocket {    private static final Logger LOG = LoggerFactory.getLogger(OnlineStateWebSocket.class);    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。    private static int onlineCount = 0;    //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识    private static CopyOnWriteArraySet<OnlineStateWebSocket> webSocketSet = new CopyOnWriteArraySet<OnlineStateWebSocket>();    //用来存放 sim 卡号对应的 连接 session 的map  ,key 为sim 卡号,value 为各个终端的session 对象    private static ConcurrentHashMap<String,ArrayList<OnlineStateWebSocket>> simSessionWebSocketMap = new ConcurrentHashMap<String,ArrayList<OnlineStateWebSocket>>();    //用来存放 httpSessionId/session 的map  ,key 为httpSessionId ,value 为各个终端的session 对象    private static ConcurrentHashMap<String,OnlineStateWebSocket> httpSessionWebSocketMap = new ConcurrentHashMap<String,OnlineStateWebSocket>();    //与某个客户端的连接会话,需要通过它来给客户端发送数据    private Session session;    private HttpSession httpSession;    /**     * 连接建立成功调用的方法     * @param session  可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据     */    @OnOpen    public void onOpen(Session session, EndpointConfig config){        this.session = session;        webSocketSet.add(this);     //加入set中        addOnlineCount();           //在线数加1        this.httpSession = (HttpSession) config.getUserProperties()                .get(HttpSession.class.getName());        //用于共享 httpSession        httpSessionWebSocketMap.put(this.httpSession.getId(), this);        System.out.println("实时数据:有新连接加入!当前在线人数为" + getOnlineCount());    }    /**     * 连接关闭调用的方法     */    @OnClose    public void onClose(){        webSocketSet.remove(this);  //从set中删除        subOnlineCount();           //在线数减1            Set<Entry<String,ArrayList<OnlineStateWebSocket>>> entrySet = simSessionWebSocketMap.entrySet();        Iterator<Entry<String, ArrayList<OnlineStateWebSocket>>> iterator = entrySet.iterator();        while(iterator.hasNext()){            Entry<String, ArrayList<OnlineStateWebSocket>> next = iterator.next();            String key = next.getKey();    //获取sim卡号            ArrayList<OnlineStateWebSocket> value = next.getValue(); //获取sim卡号对应的 连接的websocket终端            if(value.contains(this)){                if(value.size()>1){                    value.remove(this);                    simSessionWebSocketMap.replace(key, value);                }else{                    simSessionWebSocketMap.remove(key);                }            }        }        System.out.println("实时数据:有一连接关闭!当前在线人数为" + getOnlineCount());    }    /**     * 收到客户端消息后调用的方法     * @param message 客户端发送过来的消息     * @param session 可选的参数     */    @OnMessage    public void onMessage(String message, Session session) {        System.out.println("实时数据:来自客户端的消息:" + message);        String[] SimArray = message.split(",");        this.session = session;        webSocketSet.add(this);     //加入set中        for(String sim:SimArray){             ArrayList<OnlineStateWebSocket> vehicleClientList ;             if(simSessionWebSocketMap.get(sim.trim()) != null){                vehicleClientList=simSessionWebSocketMap.get(sim.trim());   //取出当前sim 卡号所对应的所有session                vehicleClientList.add(this);                simSessionWebSocketMap.replace(sim.trim(), vehicleClientList);             }else{                vehicleClientList= new ArrayList<OnlineStateWebSocket>();                vehicleClientList.add(this);                 simSessionWebSocketMap.put(sim.trim(), vehicleClientList);             }        }    }    /**     * 发生错误时调用     * @param session     * @param error     */    @OnError    public void onError(Session session, Throwable error){        System.out.println("实时数据:发生错误");        error.printStackTrace();    }    /**     * 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。     * @param message     * @throws IOException     */    public   void sendMessage(String message,HttpServletRequest request) throws IOException{        if(this.session.isOpen()){            this.session.getBasicRemote().sendText(message);            this.session.setMaxTextMessageBufferSize(102400);//          this.session.getAsyncRemote().sendText(message);        }    }    public   void sendMessage(String message) throws IOException{        if(this.session.isOpen()){            this.session.getBasicRemote().sendText(message);            this.session.setMaxTextMessageBufferSize(102400);//          this.session.getAsyncRemote().sendText(message);        }    }    public static synchronized int getOnlineCount() {        return onlineCount;    }    public static synchronized void addOnlineCount() {        OnlineStateWebSocket.onlineCount++;    }    public static synchronized void subOnlineCount() {        OnlineStateWebSocket.onlineCount--;    }    public static CopyOnWriteArraySet<OnlineStateWebSocket> getWebSocketSet() {        return webSocketSet;    }    public static void setWebSocketSet(CopyOnWriteArraySet<OnlineStateWebSocket> webSocketSet) {        OnlineStateWebSocket.webSocketSet = webSocketSet;    }}

2、RealWebsocket.java

import java.io.IOException;import java.util.ArrayList;import java.util.Iterator;import java.util.List;import java.util.Map.Entry;import java.util.Set;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.CopyOnWriteArraySet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpSession;import javax.websocket.EndpointConfig;import javax.websocket.OnClose;import javax.websocket.OnError;import javax.websocket.OnMessage;import javax.websocket.OnOpen;import javax.websocket.Session;import javax.websocket.server.ServerEndpoint;import org.slf4j.Logger;import org.slf4j.LoggerFactory;//该注解用来指定一个URI,客户端可以通过这个URI来连接到WebSocket。类似Servlet的注解mapping。无需在web.xml中配置。@ServerEndpoint(value ="/realWebsocket",configurator=GetHttpSessionConfigurator.class)public class RealWebSocket {    private static final Logger LOG = LoggerFactory.getLogger(RealLocationWebSocket.class);    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。    private static int onlineCount = 0;    //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识    private static CopyOnWriteArraySet<RealLocationWebSocket> webSocketSet = new CopyOnWriteArraySet<RealLocationWebSocket>();    //用来存放 sim 卡号对应的 连接 session 的map  ,key 为sim 卡号,value 为各个终端的session 对象    private static ConcurrentHashMap<String,ArrayList<RealLocationWebSocket>> simSessionWebSocketMap = new ConcurrentHashMap<String,ArrayList<RealLocationWebSocket>>();    //用来存放 httpSessionId/session 的map  ,key 为httpSessionId ,value 为各个终端的session 对象    private static ConcurrentHashMap<String,RealLocationWebSocket> httpSessionWebSocketMap = new ConcurrentHashMap<String,RealLocationWebSocket>();    //与某个客户端的连接会话,需要通过它来给客户端发送数据    private Session session;    private HttpSession httpSession;    /**     * 连接建立成功调用的方法     * @param session  可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据     */    @OnOpen    public void onOpen(Session session, EndpointConfig config){        this.session = session;        webSocketSet.add(this);     //加入set中        addOnlineCount();           //在线数加1        this.httpSession = (HttpSession) config.getUserProperties()                .get(HttpSession.class.getName());        //用于共享 httpSession        httpSessionWebSocketMap.put(this.httpSession.getId(), this);        System.out.println("实时数据:有新连接加入!当前在线人数为" + getOnlineCount());    }    /**     * 连接关闭调用的方法     */    @OnClose    public void onClose(){        webSocketSet.remove(this);  //从set中删除        subOnlineCount();           //在线数减1            Set<Entry<String,ArrayList<RealLocationWebSocket>>> entrySet = simSessionWebSocketMap.entrySet();        Iterator<Entry<String, ArrayList<RealLocationWebSocket>>> iterator = entrySet.iterator();        while(iterator.hasNext()){            Entry<String, ArrayList<RealLocationWebSocket>> next = iterator.next();            String key = next.getKey();    //获取sim卡号            ArrayList<RealLocationWebSocket> value = next.getValue(); //获取sim卡号对应的 连接的websocket终端            if(value.contains(this)){                if(value.size()>1){                    value.remove(this);                    simSessionWebSocketMap.replace(key, value);                }else{                    simSessionWebSocketMap.remove(key);                }            }        }        System.out.println("实时数据:有一连接关闭!当前在线人数为" + getOnlineCount());    }    /**     * 收到客户端消息后调用的方法     * @param message 客户端发送过来的消息     * @param session 可选的参数     */    @OnMessage    public void onMessage(String message, Session session) {        System.out.println("实时数据:来自客户端的消息:" + message);        String[] SimArray = message.split(",");        this.session = session;        webSocketSet.add(this);     //加入set中        for(String sim:SimArray){             ArrayList<RealLocationWebSocket> vehicleClientList ;             if(simSessionWebSocketMap.get(sim.trim()) != null){                vehicleClientList=simSessionWebSocketMap.get(sim.trim());   //取出当前sim 卡号所对应的所有session                vehicleClientList.add(this);                simSessionWebSocketMap.replace(sim.trim(), vehicleClientList);             }else{                vehicleClientList= new ArrayList<RealLocationWebSocket>();                vehicleClientList.add(this);                 simSessionWebSocketMap.put(sim.trim(), vehicleClientList);             }        }    }    /**     * 发生错误时调用     * @param session     * @param error     */    @OnError    public void onError(Session session, Throwable error){        System.out.println("实时数据:发生错误");        error.printStackTrace();    }    /**     * 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。     * @param message     * @throws IOException     */    public   void sendMessage(String message,HttpServletRequest request) throws IOException{        if(this.session.isOpen()){            this.session.getBasicRemote().sendText(message);            this.session.setMaxTextMessageBufferSize(102400);//          this.session.getAsyncRemote().sendText(message);        }    }    public   void sendMessage(String message) throws IOException{        if(this.session.isOpen()){            this.session.getBasicRemote().sendText(message);            this.session.setMaxTextMessageBufferSize(102400);//          this.session.getAsyncRemote().sendText(message);        }    }    public static synchronized int getOnlineCount() {        return onlineCount;    }    public static synchronized void addOnlineCount() {        RealLocationWebSocket.onlineCount++;    }    public static synchronized void subOnlineCount() {        RealLocationWebSocket.onlineCount--;    }    public static CopyOnWriteArraySet<RealLocationWebSocket> getWebSocketSet() {        return webSocketSet;    }    public static void setWebSocketSet(CopyOnWriteArraySet<RealLocationWebSocket> webSocketSet) {        RealLocationWebSocket.webSocketSet = webSocketSet;    }}

3、最后就是根据httpSessionId 实现 websocket session 共享了

首先就是实现 在RealWebSocket.java 中和 OnlineWebsocket 中都出现了的 GetHttpSessionConfigurator.class

import java.util.ArrayList;import java.util.List;import javax.servlet.http.HttpSession;import javax.websocket.Extension;import javax.websocket.HandshakeResponse;import javax.websocket.server.HandshakeRequest;import javax.websocket.server.ServerEndpointConfig;public class GetHttpSessionConfigurator extends ServerEndpointConfig.Configurator{    @Override    public <T> T getEndpointInstance(Class<T> clazz)            throws InstantiationException {        try {            return clazz.newInstance();        } catch (IllegalAccessException e) {            InstantiationException ie = new InstantiationException();            ie.initCause(e);            throw ie;        }    }    @Override    public String getNegotiatedSubprotocol(List<String> supported,            List<String> requested) {        for (String request : requested) {            if (supported.contains(request)) {                return request;            }        }        return "";    }    @Override    public List<Extension> getNegotiatedExtensions(List<Extension> installed,            List<Extension> requested) {        List<Extension> result = new ArrayList<>();        for (Extension request : requested) {            if (installed.contains(request)) {                result.add(request);            }        }        return result;    }    @Override    public boolean checkOrigin(String originHeaderValue) {        return true;    }    @Override    public void modifyHandshake(ServerEndpointConfig config,                                 HandshakeRequest request,                                 HandshakeResponse response)    {        HttpSession httpSession = (HttpSession)request.getHttpSession();        config.getUserProperties().put(HttpSession.class.getName(),httpSession);    }}

在上面这个过滤器里面,如果不出意外,你是会报一个空指针的,所以呢,在这里要修复一下

import javax.servlet.ServletRequestEvent;import javax.servlet.ServletRequestListener;import javax.servlet.annotation.WebListener;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpSession;@WebListenerpublic class RequestListener implements ServletRequestListener {    @Override    public void requestDestroyed(ServletRequestEvent sre) {        // TODO Auto-generated method stub    }    @Override    public void requestInitialized(ServletRequestEvent sre) {        HttpSession session = ((HttpServletRequest) sre.getServletRequest()).getSession();    }}

另外要将这个过滤器加到web.xml 中

    <listener>        <listener-class>com.vshop.sys.listener.RequestListener</listener-class>    </listener>

这样完了之后,应该就不会有什么问题了

就是通过 OnlineWebsocket.java 和 RealWebsocket .java中的httpSessionWebSocketMap 变量实现 两个 websocket Session 共享的

0 0