- 浏览: 3038 次
- 性别:
- 来自: 大连
文章分类
最新评论
闲来无事 一直想把以前做的一小块comet应用写个博客记下来
当初用的时候没找到比较好的例子 一边摸索一边鼓捣
今天写下来给需要的人参阅一下。。
Comet是基于 HTTP 长连接的“服务器推”技术
服务器推”是一种很早就存在的技术,以前在实现上主要是通过客户端的套接口,或是服务器端的远程调用。因为浏览器技术的发展比较缓慢,没有为“服务器推”的实现提供很好的支持,在纯浏览器的应用中很难有一个完善的方案去实现“服务器推”并用于商业程序。最近几年,因为 AJAX 技术的普及,以及把 IFrame 嵌在“htmlfile“的 ActiveX 组件中可以解决 IE 的加载显示问题,一些受欢迎的应用如 meebo,gmail+gtalk 在实现中使用了这些新技术;同时“服务器推”在现实应用中确实存在很多需求。因为这些原因,基于纯浏览器的“服务器推”技术开始受到较多关注,Alex Russell(Dojo Toolkit 的项目 Lead)称这种基于 HTTP 长连接、无须在浏览器端安装插件的“服务器推”技术为“Comet”。目前已经出现了一些成熟的 Comet 应用以及各种开源框架;一些 Web 服务器如 Jetty 也在为支持大量并发的长连接进行了很多改进。
一般我是极其反感看此类的简介,因为你看了半天你根本不知道他在讲什么,一堆废话,一堆术语和关键字。
大白话来讲comet就是抓住HTTP请求不释放,攥在手里,等你想要释放的时候在释放。所以就有了推的感觉。
某人给你打电话,你接起电话寒暄一阵挂断,就完成了此次事件。但是过了2分钟你想起有个事没说,你却找不到他了(没有来电显示,对方使用公共匿名电话拨打)。为了避免这种情况发生,就出现了轮训,何为轮训就是使用匿名公共电话的人一分钟给你打一次电话,以防你有事忘了说。这样在现实中就完蛋了,啥也不用干,一天就打电话得了。
换到我们互联网应用,因为他是机器,没有状态和感情,不用考虑他感受,但是这么做长久的轮训也会造成客户端浏览器假死,而被轮训的,或者说接电话的就会累死。他同时要准备接听无数个电话,迫使他做出开辟一个新的房间,弄了个总机,分成无数个分机同时准备接电话。这就是蛋疼的轮训。
而comet的意思就是,你给我打电话,OK 我接起来,一直不挂掉,没话就不说,想起来什么什么时候说,当然这么做也会有压力。。。电话费啊(服务端和客户端一直占用,不断开。。。)
罗哩叭嗦一大堆 正题来了。
先看页面代码
定义请求的服务器地址和servlet 添加上你的用户ID为后面推送做准备
这段代码是在哪找的忘了。。。识别各种浏览器添加iframe并开启事件。
木有什么好说明的。
下面是servlet的服务端代码
注释写的不是很全面 代码应该很容易懂的
在附件内我将完整的代码传上来包括页面的JS和后台的几个class。。。如果用的话直接看看没什么问题。
同样 你需要修改tomcat 目录下conf文件夹下的server.xml
当初用的时候没找到比较好的例子 一边摸索一边鼓捣
今天写下来给需要的人参阅一下。。
Comet是基于 HTTP 长连接的“服务器推”技术
引用
服务器推”是一种很早就存在的技术,以前在实现上主要是通过客户端的套接口,或是服务器端的远程调用。因为浏览器技术的发展比较缓慢,没有为“服务器推”的实现提供很好的支持,在纯浏览器的应用中很难有一个完善的方案去实现“服务器推”并用于商业程序。最近几年,因为 AJAX 技术的普及,以及把 IFrame 嵌在“htmlfile“的 ActiveX 组件中可以解决 IE 的加载显示问题,一些受欢迎的应用如 meebo,gmail+gtalk 在实现中使用了这些新技术;同时“服务器推”在现实应用中确实存在很多需求。因为这些原因,基于纯浏览器的“服务器推”技术开始受到较多关注,Alex Russell(Dojo Toolkit 的项目 Lead)称这种基于 HTTP 长连接、无须在浏览器端安装插件的“服务器推”技术为“Comet”。目前已经出现了一些成熟的 Comet 应用以及各种开源框架;一些 Web 服务器如 Jetty 也在为支持大量并发的长连接进行了很多改进。
一般我是极其反感看此类的简介,因为你看了半天你根本不知道他在讲什么,一堆废话,一堆术语和关键字。
大白话来讲comet就是抓住HTTP请求不释放,攥在手里,等你想要释放的时候在释放。所以就有了推的感觉。
某人给你打电话,你接起电话寒暄一阵挂断,就完成了此次事件。但是过了2分钟你想起有个事没说,你却找不到他了(没有来电显示,对方使用公共匿名电话拨打)。为了避免这种情况发生,就出现了轮训,何为轮训就是使用匿名公共电话的人一分钟给你打一次电话,以防你有事忘了说。这样在现实中就完蛋了,啥也不用干,一天就打电话得了。
换到我们互联网应用,因为他是机器,没有状态和感情,不用考虑他感受,但是这么做长久的轮训也会造成客户端浏览器假死,而被轮训的,或者说接电话的就会累死。他同时要准备接听无数个电话,迫使他做出开辟一个新的房间,弄了个总机,分成无数个分机同时准备接电话。这就是蛋疼的轮训。
而comet的意思就是,你给我打电话,OK 我接起来,一直不挂掉,没话就不说,想起来什么什么时候说,当然这么做也会有压力。。。电话费啊(服务端和客户端一直占用,不断开。。。)
罗哩叭嗦一大堆 正题来了。
先看页面代码
定义请求的服务器地址和servlet 添加上你的用户ID为后面推送做准备
这段代码是在哪找的忘了。。。识别各种浏览器添加iframe并开启事件。
木有什么好说明的。
var server = '<%=basePath%>SiteInfo?userId=<%=session.getAttribute("userId")%>'; var comet = { connection : false, iframediv : false, initialize: function() { if (navigator.appVersion.indexOf("MSIE") != -1) { comet.connection = new ActiveXObject("htmlfile"); comet.connection.open(); comet.connection.write("<html>"); comet.connection.write("<script>document.domain = '"+document.domain+"'"); comet.connection.write("</html>"); comet.connection.close(); comet.iframediv = comet.connection.createElement("div"); comet.connection.appendChild(comet.iframediv); comet.connection.parentWindow.comet = comet; comet.iframediv.innerHTML = "<iframe id='comet_iframe' src='"+server+"'></iframe>"; } else if (navigator.appVersion.indexOf("KHTML") != -1) { comet.connection = document.createElement('iframe'); comet.connection.setAttribute('id', 'comet_iframe'); comet.connection.setAttribute('src', server); with (comet.connection.style) { position = "absolute"; left = top = "-100px"; height = width = "1px"; visibility = "hidden"; } document.body.appendChild(comet.connection); } else { comet.connection = document.createElement('iframe'); comet.connection.setAttribute('id', 'comet_iframe'); with (comet.connection.style) { left = top = "-100px"; height = width = "1px"; visibility = "hidden"; display = 'none'; } comet.iframediv = document.createElement('iframe'); comet.iframediv.setAttribute('src', server); comet.connection.appendChild(comet.iframediv); document.body.appendChild(comet.connection); } }, //添加私人消息 //这里是回调方法 privateMessage: function(data){ // alert("有新消息!"); $.messager.anim('show',1000); $.messager.show(0,'<a href="Site_listReceive.action">您有'+data+'条新短信!</a>'); }, //退出 // onUnload: function() { // if (comet.connection) { // comet.connection = false; //} //} }//comet end <%}%> if (window.addEventListener) { window.addEventListener("load", comet.initialize, false); // window.addEventListener("unload", comet.onUnload, false); } else if (window.attachEvent) { window.attachEvent("onload", comet.initialize); // window.attachEvent("onunload", comet.onUnload); }
下面是servlet的服务端代码
注释写的不是很全面 代码应该很容易懂的
package com.gmako.web.comet; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.InputStreamReader; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.ServletResponse; import org.apache.catalina.CometEvent; import org.apache.catalina.CometProcessor; import org.springframework.context.ApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; import com.gmako.entity.UserInfo; import com.gmako.service.ISiteInformationService; import com.gmako.service.impl.SiteInformationServiceImpl; public class SiteInfoServlet extends HttpServlet implements CometProcessor { private static final long serialVersionUID = -3667180332947986301L; private static MessageSender messageSender = null; // <用户,长连接> //声明两个MAP 用来存储response和request protected static Map<String, HttpServletResponse> connections = new HashMap<String, HttpServletResponse>(); protected static Map<String, HttpServletRequest> requests = new HashMap<String, HttpServletRequest>(); private static final Integer TIMEOUT = 60 * 1000; @Override public void destroy() { messageSender.stop(); messageSender = null; } @Override public void init() throws ServletException { messageSender = new MessageSender(); Thread messageSenderThread = new Thread(messageSender, "MessageSender[" + getServletContext().getContextPath() + "]"); messageSenderThread.setDaemon(true); messageSenderThread.start(); } public void event(final CometEvent event) throws IOException, ServletException { HttpServletRequest request = event.getHttpServletRequest();//获取请求响应 HttpServletResponse response = event.getHttpServletResponse(); String userId = (String) request.getParameter("userId"); if(userId==null||"".equals(userId)){ //判断用户 return; } if (event.getEventType() == CometEvent.EventType.BEGIN) {//获取事件 event.setTimeout(Integer.MAX_VALUE);//设置过期时间 log("Begin for session: " + request.getSession(true).getId()+"userId是:"+userId); PrintWriter writer = response.getWriter(); writer .println("<!doctype html public \"-//w3c//dtd html 4.0 transitional//en\">"); writer .println("<html><head><script type=\"text/javascript\">var comet = window.parent.comet;</script></head><body>"); writer.println("<script type=\"text/javascript\">"); writer.println("var comet = window.parent.comet;"); writer.println("</script>"); writer.flush(); // for chrome if (request.getHeader("User-Agent").contains("KHTML")) { for (int i = 0; i < 100; i++) { writer.print("<input type=hidden name=none value=none>"); } writer.flush(); } System.out.println("链接的IP是:"+request.getLocalAddr()); System.out.println("链接的IP是:"+request.getHeaderNames()); if (userId != null||!("".equals(userId))) { //讲response和request放入集合中 以用户ID为key synchronized (connections) { connections.put(userId, response); } synchronized (requests) { requests.put(userId + "", request); } } } else if (event.getEventType() == CometEvent.EventType.ERROR) { log("Error for session: " + request.getSession(true).getId()+"userId是:"+userId+",非正常断开!"); if (userId != null) { synchronized (connections) { connections.remove(userId); } synchronized (requests) { requests.remove(userId); } } event.close(); } else if (event.getEventType() == CometEvent.EventType.END) { log("End for session: " + request.getSession(true).getId()+"userId是:"+userId+",正常断开!"); if (userId != null) { synchronized (connections) { connections.remove(userId); } synchronized (requests) { requests.remove(userId); } } event.close(); } } public static void send(int userId) { System.out.println("传过来的userId是" + userId); if(messageSender != null){ messageSender.send(userId + ""); //调用send方法 } } public void start() { } private class MessageSender implements Runnable { protected boolean running = true; protected final ArrayList<String> messages = new ArrayList<String>(); public void stop() { running = false; } /** * Add message for sending. 添加要发送的消息到消息队列内 */ public void send(String message) { synchronized (messages) { messages.add(message); log("Message added #messages=" + messages.size()); messages.notify(); } } public void run() { while (running) { if (messages.size() == 0) { //看队列内是否有消息 如果没有暂停该线程 try { synchronized (messages) { messages.wait(); } } catch (InterruptedException e) { // Ignore } } String[] pendingMessages = null; synchronized (messages) { //将消息集合内容传递给该数组 并清空集合 以便线程停止 pendingMessages = messages.toArray(new String[0]); messages.clear(); } if (connections == null) { //判断response集合是否有等待的 try { synchronized (this) { wait(); } } catch (InterruptedException e) { // Ignore } } if (requests == null) { //判断request集合是否有等待的 try { synchronized (this) { wait(); } } catch (InterruptedException e) { // Ignore } } HttpServletResponse res = null; PrintWriter writer = null; for (int j = 0; j < pendingMessages.length; j++) { System.out.println(requests.size()); System.out.println("消息队列里面的值是:" + pendingMessages[j]); if (connections.get(pendingMessages[j]) != null) { //以用户ID为key取出该用户的response 并直接打印 res = connections //由于前段为AJAX请求 所以可以直接通过该回调获取打印内容 .get(pendingMessages[j]); try { writer = res.getWriter(); } catch (IOException e) { e.printStackTrace(); } System.out.println("我发了消息!"); writer .print("<script type=\"text/javascript\">"); writer.println("comet.privateMessage('" + 1 + "');"); writer.print("</script>"); writer.flush(); log("Writing:" + "发送了一条推送给" + pendingMessages[j].toString()); writer.flush(); // writer.close(); } } log("Closing connection"); } } }
在附件内我将完整的代码传上来包括页面的JS和后台的几个class。。。如果用的话直接看看没什么问题。
同样 你需要修改tomcat 目录下conf文件夹下的server.xml
<Connector port="8088" connectionTimeout="20000" protocol="org.apache.coyote.http11.Http11NioProtocol" redirectPort="8443" useBodyEncodingForURI="true" URIEncoding="utf-8" />
- comet.zip (3.5 KB)
- 下载次数: 53
发表评论
文章已被作者锁定,不允许评论。
相关推荐
comet4j开发指南 Comet4J(Comet for Java)是一个纯粹基于AJAX(XMLHTTPRequest)的服务器推送框架
tomcat实现comet例子,实现后台产生每隔几秒产生随机数,前台不刷新显示。tomcat实现comet例子,实现后台产生每隔几秒产生随机数,前台不刷新显示。tomcat实现comet例子,实现后台产生每隔几秒产生随机数,前台不...
通过tomcat服务器,向网页发送即时消息。comet的小例子,可以直接运行,用tomcat6.0。
comet4j,comet4j实例,实例代码,可直接运行
comet框架例子项目,自己写的一个参考例子供大家学习
comet4j完整包、comet4j.js、comet4j-tomcat6.jar、comet4j-tomcat7.jar
comet4j消息推送所需的comet4j-tomcat7.jar包,comet4j-tomcat7.jar
comet4j-tomcat7.jar comet4j.js Comet4J(Comet for Java)是一个纯粹基于AJAX(XMLHTTPRequest)的服务器推送框架
comet4j-tomcat6、comet4j-tomcat7 的jar包和comet4j.js资源下载
Comet4J(Comet for Java)是一个纯粹基于AJAX(XMLHTTPRequest)的服务器推送框架,消息以JSON方式传递,具备长轮询、长连接、自动选择三种工作模式 文件包含comet4j-tomcat6.jar , comet4j-tomcat7.jar , comet4j.js...
包含comet4j-tomcat6.jar,comet4j-tomcat7.jar,comet4j.js,以及官网demo:comet4j-tomcat6-demo.war
java-comet
ssh框架整合comet实现即时通信的简单实例,ssh框架整合comet实现即时通信的简单实例
comet4j消息推送所需的comet4j.js文件、comet4j-tomcat6.jar、comet4j-tomcat7.jar包
comet4j-tomcat6,comet4j-tomcat7,comet4j.js,comet4j.js,以及一个样例
comet4j的简单demo项目,此项目在eclipse下创建,导入后参照...代码中注释详细,适用于初学者入门。 本项目使用的是comet4j-tomcat6.jar,所以请在tomcat6下运行。如果需要在tomcat7下运行,请自行下载对应的jar包。
有关comet4j的所有jar包都在这里了,tomcat6,tomcat7的都有,js和一个demo.war案例
asp.net comet例子,基于ajax的长联技术, 注:此版本只有ff下正常 ie下可以用iframe实现 http://www.dotnetdev.cn