dwr实现javaweb实时聊天通讯网页版常见问题及解决方案

来源:互联网 发布:上汽集团 知乎 编辑:程序博客网 时间:2024/05/15 03:47

使用dwr实现javaweb实时聊天通讯网页版的时候可能会遇到以下问题:

1、因为每次刷新页面都创建一个scriptsession而产生发送一条消息接收方收到多条消息的问题;

问题分析:

        scriptsession不同于普通的session,普通的session在我们第一次访问网站的时候创建,再次访问此网站不会再次创建session(session有效期内,且没有关闭浏览器或清除缓存),而scriptsession则是每打开一个页面或者刷新页面都会生成新的scriptsession,并且短时间内原有的scriptsession并没有销毁(有的文档上说的是5分钟),所以会同时存在多个scriptsession,一般的定向发送消息会使用Browser.withAllSessionsFiltered(ScriptSessionFilter filter, Runnable task)该方法,具体实现如下:

Browser.withAllSessionsFiltered(new ScriptSessionFilter() {
                public boolean match(ScriptSession session) {
                    if (session.getAttribute("userId") == null)  
                        return false;  
                    else   
                        return (sendtoId.indexOf((String)session.getAttribute("userId"))>=0);
                }
            }, new Runnable() {
                private ScriptBuffer script = new ScriptBuffer();
                public void run() {
                    Collection<ScriptSession> sessions = Browser.getTargetSessions();  
                      
                    for (ScriptSession scriptSession : sessions) {  
                        script.appendCall("showTotalNum", totalUnCheckedNum);
                        scriptSession.addScript(script);  
                    }
                }
            });

所以在存在多个scriptsession的情况下就回出现发送一条消息但是接收到多条消息的问题。

解决方案:

        自己创建dwr监听类DWRScriptSessionListener.java,在初始化的时候使用自己的监听类,自己的监听类中定义Map来保存有效的scriptsession,定向发送消息时从此Map中获取scriptsession,完成发送,就能避免重复发送

DWRScriptSessionListener.java代码如下:


import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpSession;

import org.directwebremoting.ScriptSession;
import org.directwebremoting.WebContextFactory;
import org.directwebremoting.event.ScriptSessionEvent;
import org.directwebremoting.event.ScriptSessionListener;

import com.ches.bean.UnitRegister;

public class DWRScriptSessionListener implements ScriptSessionListener {

    // 维护一个Map key为session的Id, value为ScriptSession对象
    public static final Map<String, ScriptSession> scriptSessionMap = new HashMap<String, ScriptSession>();

    /**
     * ScriptSession创建事件
     */
    @Override
    public void sessionCreated(ScriptSessionEvent event) {
        HttpSession session = WebContextFactory.get().getSession();
        UnitRegister user = (UnitRegister) session.getAttribute("user");
        ScriptSession scriptSession = event.getSession();
        if(user!=null){
            scriptSession.setAttribute("userId", user.getId());
            scriptSessionMap.put(user.getId(), scriptSession); // 添加scriptSession
        }else{
            scriptSession.setAttribute("userId", session.getId());
            scriptSessionMap.put(session.getId(), scriptSession); // 添加scriptSession
        }
    }

    /**
     * ScriptSession销毁事件
     */
    @Override
    public void sessionDestroyed(ScriptSessionEvent event) {
        if(event.getSession() != null&&!event.getSession().isInvalidated()){
            event.getSession().invalidate();
        }
    }

    /**
     * 获取所有ScriptSession
     */
    public static Map<String, ScriptSession> getScriptSessions() {
        return scriptSessionMap;
    }
}

如何使用自己定义的监听类:

原始监听的代码为:

public void init() throws ServletException {  
 
        try {  
            Container container = ServerContextFactory.get().getContainer();  
            ScriptSessionManager manager = container.getBean(ScriptSessionManager.class);  
            ScriptSessionListener listener = new ScriptSessionListener() {

                @Override
                public void sessionCreated(ScriptSessionEvent event) {
                    HttpSession session = WebContextFactory.get().getSession();
                    UnitRegister user = (UnitRegister) session.getAttribute("user");
                    if(user!=null){
                        event.getSession().setAttribute("userId", user.getId());
                    }else{
                        event.getSession().setAttribute("userId", session.getId());
                    }
                }

                @Override
                public void sessionDestroyed(ScriptSessionEvent arg0) {
                    
                }  
            };  

            manager.addScriptSessionListener(listener);  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }
使用自己的监听类则改为:

public void init() throws ServletException {  
        try {  
            Container container = ServerContextFactory.get().getContainer();  
            ScriptSessionManager manager = container.getBean(ScriptSessionManager.class);  
            ScriptSessionListener listener = new DWRScriptSessionListener();
            manager.addScriptSessionListener(listener);  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }

发送信息处的代码处理为:

Browser.withAllSessionsFiltered(new ScriptSessionFilter() {
                public boolean match(ScriptSession session) {
                    return true;
                }
            }, new Runnable() {
                private ScriptBuffer script = new ScriptBuffer();
                public void run() {
                    Map<String, ScriptSession> sessions = DWRScriptSessionListener.getScriptSessions();
                    ScriptSession scriptSession = sessions.get(sendtoId);
                    if(scriptSession!=null){
                        script.appendCall("showMessage",totalUnCheckedNum , unCheckedNum, thisId, thisName, sendtoId, sendtoName, sendMessage);
                        scriptSession.addScript(script);  
                    }
                }
            });

2、关闭聊天界面后无法收到消息

问题分析:

解决了问题1之后,按照以上的思路要想在其他页面显示和反馈收到的消息将无法实现,因为只给最后的保存在自定义的map中的scriptsession发送消息,就只能在打开的对话页面接收消息,关闭对话页面继续浏览其他页面则无法接收消息。

解决方案:

同时使用原始的发送消息策略和问题1中改造后的发送消息策略,即

(1)Browser.withAllSessionsFiltered(new ScriptSessionFilter() {
                public boolean match(ScriptSession session) {
                    if (session.getAttribute("userId") == null)  
                        return false;  
                    else   
                        return (sendtoId.indexOf((String)session.getAttribute("userId"))>=0);
                }
            }, new Runnable() {
                private ScriptBuffer script = new ScriptBuffer();
                public void run() {
                    Collection<ScriptSession> sessions = Browser.getTargetSessions();  
                      
                    for (ScriptSession scriptSession : sessions) {  
                        script.appendCall("showTotalNum", totalUnCheckedNum);
                        scriptSession.addScript(script);  
                    }
                }
            });

(2)Browser.withAllSessionsFiltered(new ScriptSessionFilter() {
                public boolean match(ScriptSession session) {
                    return true;
                }
            }, new Runnable() {
                private ScriptBuffer script = new ScriptBuffer();
                public void run() {
                    Map<String, ScriptSession> sessions = DWRScriptSessionListener.getScriptSessions();
                    ScriptSession scriptSession = sessions.get(sendtoId);
                    if(scriptSession!=null){
                        script.appendCall("showMessage",totalUnCheckedNum , unCheckedNum, thisId, thisName, sendtoId, sendtoName, sendMessage);
                        scriptSession.addScript(script);  
                    }
                }
            });

说明:1、没有放在自定义map中的scriptsession在销毁之前一直存在的。

2、发送消息策略(1)和(2)中的回调函数名称要区分,不能使用同一名称。


原创粉丝点击