Java聊天室[长轮询]

今天看到有人分享java实现的聊天室,想起很久以前还在热衷于java的时候也做过一个web聊天室,不拿出来晒晒,可能再也不为人知了,单纯是一个兴趣作品,稳定性不好,也没有考虑连接数和并发的问题,拿出来博大家一笑吧,项目我已改为maven管理;
     有一些没有修复的bug,比如SesseionManager 里的sessionCache 会只增不减等等,每一个用户一个Session实例,一个消息buffer(MessageQueue)来缓存未收到的消息,有SessionManager来管理,Dispatcher只是实现了 “广播”  消息,木有“多播”/“单播”,有兴趣的可以完善;下面是SessionManager,最长的一个代码文件了~,见笑了,用户下线是线程检查的,并不是很灵敏;
          ps:web聊天目前最简单的使用node.js的socket.io实现;
?1.图片
????
?2. [代码][Java]代码   
package com.easyim.core;
 
import java.util.Collection;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
 
import com.easyim.util.EasyUtil;
import com.easyim.util.Log;
 
/**
 * Session 管理类(单例)
 * @author xl
 */
public class SessionManager {
     
    /**
     * 缓存实例
     */
    private static SessionManager instance = null;
     
    /**
     * 存储session,Map<Session Id,Session>
     */
    private ConcurrentHashMap<String, Session>  sessions = new ConcurrentHashMap<String, Session>();
     
    /**
     * 计划任务,用于指定时间检查session是否有效
     */
    private Timer timer;
    private final long TIMER_INTERVAL_MILLIS = Config.getLongProperty(Protocol.TIMER_INTERVAL_MINS.toString())*1000;
    /**
     * SessionManager同步监视锁
     */
    private final Object lock = new Object();
     
     
    /**
     * 缓存sessions,防止检查时,无任何add/remove操作仍然遍历sessions
     */
    private Session[] sessionCache = new Session[0];
     
    /**
     * 是否重新缓存(自动设置)
     */
    private volatile boolean sessionCacheDirty = false;
     
    /**
     * 
     */
    protected SessionManager(){
         
    }
     
    /**
     * 获得SessionManager实例
     * @return
     */
    public static SessionManager getInstance(){
        if(instance==null){
            instance = new SessionManager();
        }
        return instance;
    }
     
    public Collection<Session> getSessions(){
        return sessions.values();
    }
     
    /**
     * 添加Session
     */
    public void addSession(Session session){
        sessions.put(session.getId(), session);
        sessionCacheDirty = true;
        Log.info("SessionManager add Session:"+session.getId()+",IP:"+session.getAddress());
    }
     
    /**
     * 获得Session
     * @param id Session Id
     * @return Session
     */
    public Session getSession(String id){
        return sessions.get(id);
    }
     
    /**
     * 指定id对应Session是否存在
     * @param id 指定Session Id
     * @return true/false
     */
    public boolean hasSession(String id){
        return sessions.containsKey(id);
    }
     
    /**
     * 移除并返回Session
     * @param session Session
     */
    public void removeSession(Session session){
        Session nsession = sessions.remove(session.getId());
        if(nsession!=null){http://www.bizhizu.cn/shouhui/?
            Log.info("SessionManager remove Session:"+nsession.getId()+",IP:"+nsession.getAddress());
        }手绘图片
        sessionCacheDirty = true;
         
    }
    /**
     * sessions 长度
     * @return 长度
     */
    public int getSize(){
        return sessions.size();
    }
     
    /**
     * 遍历执行Seesion并调用ApplyMethod invoke执行
     * >>添加缓存支持
     * @param method
     */
    public void apply(ApplyMethod method){
//          Iterator<Session> iterator = sessions.values().iterator();
//          Session session = null;
//          while (iterator.hasNext()) {
//              session = iterator.next();
//              try {
//                  method.invoke(session);
//              } catch (Exception e) {
//                  Log.warn("SessionManager apply invoke 方法执行出错:"+e);
//              }
//          }
            //更新缓存
            if(sessionCacheDirty){
                //TODO (easyim)sessionCache的只增不减...
                sessionCache = sessions.values().toArray(sessionCache);
                //设置状态为fasle,防止再次更新
                sessionCacheDirty=false;
            }
             
            //需要synchronized吗?
             
            //遍历,传递给 method对象的invoke方法执行(必须从0开始遍历)
            for (int i = 0; i < sessionCache.length; i++) {
                //为 null,退出循环
                if(sessionCache[i]==null){
                    break;
                }
                try {
                    method.invoke(sessionCache[i]);
                } catch (Exception e) {
                    Log.warn("SessionManager apply invoke 方法执行出错:",e);
                }
            }
    }
     
    public void start(){
        if (timer!=null) {
            stop();
        }
        timer = new Timer(false);
        timer.schedule(new CheckTimerTask(), TIMER_INTERVAL_MILLIS, TIMER_INTERVAL_MILLIS);
        Log.info("CheckTimerTask started; interval=" + TIMER_INTERVAL_MILLIS + "ms");
         
    }
     
    public void stop(){
        if (timer != null) {
            timer.cancel();
            timer = null;
        }
        sessions.clear();
        Log.info("CheckTimerTask stopped");
    }
     
    /**
     * apply 调用方法接口
     * @author xl
     *
     */
    public static interface ApplyMethod{
 
        public void invoke(Session session);
    }
     
    /**
     * 检查Session是否有效
     * @author xl
     *
     */
    private class CheckTimerTask extends TimerTask implements ApplyMethod{
         
        //private final long MAXWAITTIME_MILLIS = Config.getLongProperty(Protocol.MAXWAITTIME_MILLIS.toString());
         
        private long lastRun = EasyUtil.now();
        private long delta;
         
        @Override
        public void run() {
            long now = EasyUtil.now();
            //按理 delta>=TIMER_INTERVAL_MILLIS
            delta = now - lastRun;
            lastRun=now;
            getInstance().apply(this);
            if(Log.getLogger().isDebugEnabled()){
                Log.debug("CheckTimerTask,时间:"+EasyUtil.dateFormat()+", sessions size:"+sessions.size()+" ,cache size:"+sessionCache.length);
            }
        }
 
        @Override
        public void invoke(Session session) {
            session.less(delta);
            if(session.isExpired()){
                Log.info("CheckTimerTask: remove Session");
                session.remove();
            }
        }
         
    }
     
}
?

Java聊天室[长轮询]

时间: 2024-11-01 13:43:13

Java聊天室[长轮询]的相关文章

php+ajax长轮询实现web即时聊天

web im的实现方式有很多种: 1.普通轮询,原理通过js定时重复发送ajax请求服务端,获取数据后显示. 2.长轮询,ajax请求服务端,服务端有数据会立即返回,服务端无数据时,会一直等待,直到有数据了才立即范围. 3.socket长连接. 特征分析: 方法1:实现起来最容易,定时重复请求服务端会产生无意义的http连接,消耗服务端资源,实时性较差. 方法2:实现起来较容易,会减少无效的ajax请求产生的http连接,能即时返回数据,但服务端会一直挂着,会消耗一定的资源,处理并发能力不强,比

ajax长轮询实现即时聊天室

前段js: //处理ajax长轮询 $(function(){ ask_order(); function ask_order(){ var ask_action = "{:U('index/order_commet',array('time'=>10,'desk_id'=>$desk_id))}"; $.ajax({                    type:"GET",                    dataType:"jso

Web 通信 之 长连接、长轮询(long polling)(转载)

基于HTTP的长连接,是一种通过长轮询方式实现"服务器推"的技术,它弥补了HTTP简单的请求应答模式的不足,极大地增强了程序的实时性和交互性. 一.什么是长连接.长轮询? 用通俗易懂的话来说,就是客户端不停的向服务器发送请求以获取最新的数据信息.这里的“不停”其实是有停止的,只是我们人眼无法分辨是否停止,它只是一种快速的停下然后又立即开始连接而已. 二.长连接.长轮询的应用场景 长连接.长轮询一般应用与WebIM.ChatRoom和一些需要及时交互的网站应用中.其真实案例有:WebQQ

Web 通信 之 长连接、长轮询(long polling)

基于HTTP的长连接,是一种通过长轮询方式实现"服务器推"的技术,它弥补了HTTP简单的请求应答模式的不足,极大地增强了程序的实时性和交互性. 一.什么是长连接.长轮询? 用通俗易懂的话来说,就是客户端不停的向服务器发送请求以获取最新的数据信息.这里的“不停”其实是有停止的,只是我们人眼无法分辨是否停止,它只是一种快速的停下然后又立即开始连接而已. 二.长连接.长轮询的应用场景 长连接.长轮询一般应用与WebIM.ChatRoom和一些需要及时交互的网站应用中.其真实案例有:WebQQ

Web 通信 之 长连接、长轮询(转)

Web 通信 之 长连接.长轮询(long polling) 基于HTTP的长连接,是一种通过长轮询方式实现"服务器推"的技术,它弥补了HTTP简单的请求应答模式的不足,极大地增强了程序的实时性和交互性. 一.什么是长连接.长轮询? 用通俗易懂的话来说,就是客户端不停的向服务器发送请求以获取最新的数据信息.这里的"不停"其实是有停止的,只是我们人眼无法分辨是否停止,它只是一种快速的停下然后又立即开始连接而已. 二.长连接.长轮询的应用场景 长连接.长轮询一般应用与W

轮询、长轮询与Web Socket的前端实现页面数据实时

Web Socket 应用场景:实现即时通讯:如股票交易行情分析.聊天室.在线游戏等,替代轮询和长轮询 1.轮询 轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP request,然后由服务器返回最新的数据给客户端的浏览器.这种传统的HTTP request 的模式带来很明显的缺点 – 浏览器需要不断的向服务器发出请求,然而HTTP request 的header是非常长的,里面包含的有用数据可能只是一个很小的值,这样会占用很多的带宽. var xhr = new XMLHtt

web长连接,长轮询(long polling)

Web 通信 之 长连接.长轮询(long polling) 基于HTTP的长连接,是一种通过长轮询方式实现"服务器推"的技术,它弥补了HTTP简单的请求应答模式的不足,极大地增强了程序的实时性和交互性. 一.什么是长连接.长轮询? 用通俗易懂的话来说,就是客户端不停的向服务器发送请求以获取最新的数据信息.这里的“不停”其实是有停止的,只是我们人眼无法分辨是否停止,它只是一种快速的停下然后又立即开始连接而已. 二.长连接.长轮询的应用场景 长连接.长轮询一般应用与WebIM.ChatR

Web 通信 之 长连接、长轮询(long polling)(ZZ)

http://feilong.me/2011/07/talk-about-polling-and-long-polling 轮询(Polling)是指不管服务器端有没有更新,客户端(通常是指浏览器)都定时的发送请求进行查询,轮询的结果可能是服务器端有新的更新过来,也可能什么也没有,只是返回个空的信息.不管结果如何,客户端处理完后到下一个定时时间点将继续下一轮的轮询. 推送或叫长连接(Long-Polling)的服务其客户端是不做轮询的,客户端在发起一次请求后立即挂起,一直到服务器端有更新的时候,

实现web消息推送的技术和采用长轮询corundumstudio介绍

实时消息的推送,PC端的推送技术可以使用socket建立一个长连接来实现.传统的web服务都是客户端发出请求,服务端给出响应.但是现在直观的要求是允许特定时间内在没有客户端发起请求的情况下服务端主动推送消息到客户端. 有哪些可以实现web消息推送的技术: 不断地轮询(俗称“拉”,polling)是获取实时消息的一个手段:Ajax 隔一段时间(通常使用 JavaScript 的 setTimeout 函数)就去服务器查询是否有改变,从而进行增量式的更新.但是间隔多长时间去查询成了问题,因为性能和即