`
浮生过半
  • 浏览: 3131 次
  • 性别: Icon_minigender_1
  • 来自: 大连
社区版块
存档分类
最新评论

comet初级入门指南

阅读更多
闲来无事 一直想把以前做的一小块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并开启事件。
  木有什么好说明的。
 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" />

分享到:
评论
发表评论

文章已被作者锁定,不允许评论。

相关推荐

    comet4j开发指南

    Comet4J开发指南 Comet4J是一个专为Java平台设计的服务器推送技术框架,它充分利用了AJAX(XMLHttpRequest)技术,实现了一种高效、实时的双向通信机制。在传统的HTTP协议中,服务器与客户端的交互是基于请求-响应...

    comet4j 所需js以及comet4j-tomcat6.jar、comet4j-tomcat7.jar包

    综上所述,这个压缩包包含的`comet4j.js`、`comet4j-tomcat6.jar`和`comet4j-tomcat7.jar`是实现基于Java的Comet4j实时通信框架的关键组件。它们分别负责客户端的JavaScript交互、在Tomcat服务器上的集成和支持,为...

    comet demo 向客户端推送例子

    这个"comet demo"是一个展示如何在Java环境下利用Tomcat服务器实现Comet技术的实例。Tomcat 6.0是Apache软件基金会开发的开源Servlet容器,支持各种Java Web应用的部署,包括Comet技术。 首先,Comet的核心在于保持...

    comet4j.jar

    Comet4j是一个Java库,专门用于实现Comet技术,这是一种服务器向客户端推送数据的Web应用程序设计模式。Comet技术打破了传统的HTTP请求-响应模型,允许服务器在客户端保持持久连接,从而实现实时数据更新。这在需要...

    comet框架例子项目

    在本"Comet框架例子项目"中,我们可以深入理解并学习如何利用Comet技术构建实时通信的应用。 Comet的核心理念是通过长时间保持一个HTTP连接来实现服务器到客户端的数据推送,而不是每次有新数据时都创建新的连接。...

    comet套件(comet4j-tomcat6/7.jar、comet4j.js)

    Comet4J是一款针对Java平台的长连接技术框架,它主要设计用于实现高效的服务器推送技术。在Web开发中,服务器通常使用HTTP协议与客户端进行通信,而HTTP协议是基于请求-响应模型的,即客户端发起请求,服务器返回...

    comet4j实例

    Comet4j是一个Java库,专门用于实现Comet技术,这是一种服务器推送技术,允许服务器向客户端实时推送数据,而不仅仅是响应客户端的请求。在Web应用中,这种技术常用于实现聊天室、股票报价、在线游戏等实时交互功能...

    web推送 comet技术

    在MyEclipse集成开发环境中,你可以创建一个新的Web项目,导入`comet4j`库,然后按照Comet4J的API和文档编写服务器端的推送逻辑。客户端通常需要JavaScript来接收并处理服务器推送的数据,这可能涉及到AJAX或者...

    catalina-comet.jar

    【Catalina-Comet.jar】是Apache Tomcat服务器中用于支持Comet技术的一个关键组件。Comet是一种在Web开发中实现服务器推送技术的方法,它允许服务器主动向客户端发送数据,而不仅仅是响应客户端的请求。这种技术对于...

    Python库 | comet_ml-2.0.12.tar.gz

    《Python库Comet_ml-2.0.12:跟踪、优化与实验管理》 在IT行业中,Python作为一门强大的开发语言,拥有丰富的库支持,其中Comet_ml就是一款专为机器学习和深度学习项目提供实验跟踪、模型优化以及协作功能的库。...

    comet4j完整包

    【标题】"comet4j完整包"是一个与Web服务端推送技术相关的软件包,它包含了一系列用于实现实时通信的组件和库。这个包旨在帮助开发者构建基于Comet技术的应用,以提供高效的、双向的服务器到客户端的数据传输。 ...

    tomcat实现comet例子 comet tomcat 随机数

    tomcat实现comet例子,实现后台产生每隔几秒产生随机数,前台不刷新显示。tomcat实现comet例子,实现后台产生每隔几秒产生随机数,前台不刷新显示。tomcat实现comet例子,实现后台产生每隔几秒产生随机数,前台不...

    comet4j 简单例子+文档

    提供的文档应该包含了Comet4j的安装指南、API参考、示例代码和最佳实践。这些资源对于理解和使用Comet4j框架至关重要,可以帮助开发者快速上手。 5. **jar文件**: 包含的jar文件是Comet4j的库文件,需要将其添加...

    comet4j-tomcat6.jar和comet4j-tomcat7.jar和comet4j.js

    标题中的"comet4j-tomcat6.jar"和"comet4j-tomcat7.jar"是针对Tomcat 6和7版本的特定兼容库。这两个JAR文件包含了Comet4j的核心组件,使得开发者能够在这些版本的Tomcat上部署和运行支持Comet的Web应用程序。它们...

    comet demo

    "Comet Demo" 是一个展示Comet技术实际应用的示例项目,用户可以下载并运行来体验其功能。 在描述中提到,"Comet下载既可以运行",这意味着这个Demo是可执行的,用户下载后无需额外的构建或配置步骤,可以直接启动...

    配置tomcat支持comet

    【标题】:“配置Tomcat支持Comet” 在Web开发中,传统的HTTP协议是基于请求-响应模型的,服务器端等待客户端发起请求,然后响应。但有些应用,如实时聊天、股票更新、在线游戏等,需要服务器能够主动推送数据到...

    asp.net comet例子

    ASP.NET Comet是一个技术概念,它涉及到了Web应用程序中的实时通信,特别是服务器向客户端推送数据的能力。在传统的HTTP协议中,服务器通常在客户端发起请求时才响应,而在Comet模式下,服务器可以保持一个连接开放...

    C#Web即时通讯Comet框架

    **C# Web即时通讯Comet框架详解** 在Web开发中,传统的HTTP协议是基于请求-响应模型的,这种模式在处理实时性需求时显得力不从心,因为服务器只有在接收到客户端的请求后才会返回数据。为了实现Web即时通讯(Web ...

Global site tag (gtag.js) - Google Analytics