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聊天室[长轮询]的更多相关文章
- php websocket-网页实时聊天之PHP实现websocket(ajax长轮询和websocket都可以时间网络聊天室)
php websocket-网页实时聊天之PHP实现websocket(ajax长轮询和websocket都可以时间网络聊天室) 一.总结 1.ajax长轮询和websocket都可以时间网络聊天室 ...
- 分享一个基于长连接+长轮询+原生的JS及AJAX实现的多人在线即时交流聊天室
实现网页版的在线聊天室的方法有很多,在没有来到HTML5之前,常见的有:定时轮询.长连接+长轮询.基于第三方插件(如FLASH的Socket),而如果是HTML5,则比较简单,可以直接使用WebSoc ...
- 使用轮询&长轮询实现网页聊天室
前言 如果有一个需求,让你构建一个网络的聊天室,你会怎么解决? 首先,对于HTTP请求来说,Server端总是处于被动的一方,即只能由Browser发送请求,Server才能够被动回应. 也就是说,如 ...
- 网页实时聊天之js和jQuery实现ajax长轮询
众所周知,HTTP协议是无状态的,所以一次的请求都是一个单独的事件,和前后都没有联系.所以我们在解决网页实时聊天时就遇到一个问题,如何保证与服务器的长时间联系,从而源源不段地获取信息. 一直以来的方式 ...
- [转]网页实时聊天之js和jQuery实现ajax长轮询 PHP
网页实时聊天之js和jQuery实现ajax长轮询 众所周知,HTTP协议是无状态的,所以一次的请求都是一个单独的事件,和前后都没有联系.所以我们在解决网页实时聊天时就遇到一个问题,如何保证与服务器的 ...
- Web 通信 之 长连接、长轮询(转)
Web 通信 之 长连接.长轮询(long polling) 基于HTTP的长连接,是一种通过长轮询方式实现"服务器推"的技术,它弥补了HTTP简单的请求应答模式的不足,极大地增强 ...
- Web 通信 之 长连接、长轮询(long polling)
基于HTTP的长连接,是一种通过长轮询方式实现"服务器推"的技术,它弥补了HTTP简单的请求应答模式的不足,极大地增强了程序的实时性和交互性. 一.什么是长连接.长轮询? 用通俗易 ...
- Web 通信 之 长连接、长轮询(long polling)(转)
基于HTTP的长连接,是一种通过长轮询方式实现"服务器推"的技术,它弥补了HTTP简单的请求应答模式的不足,极大地增强了程序的实时性和交互性. 一.什么是长连接.长轮询? 用通俗易 ...
- 长轮询和Comet
长轮询方式是由前端定时发起AJAX请求,若请求到数据则把数据显示出来. comet方式是由客户端与服务器端发起一个长连接,然后客户端通过监听事件的方式,来对服务器端返回的数据作出响应和处理. 实时性要 ...
随机推荐
- PageHelper分页工具
<a>共${page.total}件商品</a> <a>共${page.pages}页</a> <a>当前第${page.pag ...
- Ubuntu 16.04下安装WineHQ
说明: 1.Wine和WIneHQ没什么区别,新版和旧版的问题. 2.安装了深度的Wine包也可以和WineHQ一起兼容,因为深度的应用名已经加了deepin前缀,所以不冲突. 3.安装了Wine之后 ...
- 邁向IT專家成功之路的三十則鐵律 鐵律四:IT人快速成長之道-複製
相信您一定看到過現今有許多各行各業的成功人士,他們最初都是從複製別人的成功經驗開始的,就算是一位知名的歌手,有許多都是在未成名以前,先行模仿知名歌手的唱腔.舞蹈.服裝等等開始的,然後在慢慢經過自我努力 ...
- 【iOS开发-58】tableView初识:5个重要方法的使用和2种样式的差别
创建一个tableView,直接拖拽放在storyboard里面就可以. (1)先创建一个数据模型类WSCarGroup,在WSCarGroup.h文件里: #import <Foundatio ...
- PS 如何使用钢笔工具
1.钢笔工具属于矢量绘图工具,其优点是可以勾画平滑的曲线,在缩放或者变形之后仍能保持平滑效果. 2.钢笔工具画出来的矢量图形称为路径,路径是矢量的路径允许是不封闭的开放状,如果把起点与终点重合绘制就可 ...
- 在pypy环境中运行odoo8
PyPy是一个独立的解析器, 通过即时编译(JIT,Just-in-time)代码避免逐行解释执行来提升运行速度的(将编译过的行代码缓存起来,从而加快速度).我们一般使用的Python一般是使用C实现 ...
- C++虚继承的概念(转)
http://blog.csdn.net/wangxingbao4227/article/details/6772579 C++中虚拟继承的概念 为了解决从不同途径继承来的同名的数据成员在内存中有不同 ...
- 网络通讯框架MINA和XSCOCKET的简单比较
http://www.blogjava.net/ghostdog/archive/2008/06/10/MinaVsXsocket.html实在无聊,考虑把当前应用的通讯模式由http移植为socke ...
- gulp-gulpfile.js语法说明
关于gulpfile文件: 直接上代码吧!! /*! * gulp * $ npm install gulp gulp-ruby-sass gulp-cached gulp-uglify gulp-r ...
- [转] git clone 远程分支
git clone只能clone远程库的master分支,无法clone所有分支,解决办法如下: 找一个干净目录,假设是git_work cd git_work git clone http://my ...