欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

学习笔记-Comet

程序员文章站 2022-05-06 15:10:30
...
Comet - 使用 HTTP 长连接、无须浏览器安装插件的两种“服务器推”方案:
  1、基于 AJAX 的长轮询方式;
  2、基于 iframe 及 htmlfile 的流方式。
  开源的 Comet 框架-pushlet,提供了以上两种方式的封装。

Ajax长轮询,客户端发起XMLHttpRequest请求,服务端阻塞请求,直到有数据返回,客户端处理完响应后,断开链接,然后马上重新建立链接。 
   1、服务器端会阻塞请求直到有数据传递或超时才返回。
   2、客户端 JavaScript 响应处理函数会在处理完服务器返回的信息后,再次发出请求,重新建立连接。
   3、当客户端处理接收的数据、重新建立连接时,服务器端可能有新的数据到达;这些信息会被服务器端保存直到客户端重新建立连接,客户端会一次把当前服务器端所有的信息取回。

图 2. 基于长轮询的服务器推模型
学习笔记-Comet
            
    
    博客分类: 学习笔记 Comet长链接服务器推 


基于 Iframe 及 htmlfile 的流(streaming)方式
iframe 是很早就存在的一种 HTML 标记, 通过在 HTML 页面里嵌入一个隐蔵帧,然后将这个隐蔵帧的 SRC 属性设为对一个长连接的请求,服务器端就能源源不断地往客户端输入数据。

图 3. 基于流方式的服务器推模型

学习笔记-Comet
            
    
    博客分类: 学习笔记 Comet长链接服务器推 

Tomcat6支持Comet,基于Stream方式,提供CometProcessor和CometEvent接口,详情请见http://tomcat.apache.org/tomcat-6.0-doc/aio.html。注意:servlet实现CometProcessor接口后不用写doGet,doPoset方法,所有事件在BEGIN,READ,END,ERROR方法中实现。
使用tomcat提供的comet api之前,需要增加对NIO的支持,在server.xml里边修改connector:
<connector protocol="org.apache.coyote.http11.Http11NioProtocol" port="8080" redirectport="8443" connectiontimeout="20000">

Jetty Comet实现机制
客户端请求挂入,如果当时没有消息,通过Continuation.suspend挂起,也可设置超时时间;当有消息时,Continuation.resume,重新执行原请求,将消息发送出去;请求结束,客户端重新请求挂入;在下一次请求挂入之前,产生的消息可以堆积起来,下一次挂入时,统一发送给客户端。
提供一个简单实现例子:
package cn.restful.jetty;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.continuation.Continuation;
import org.eclipse.jetty.continuation.ContinuationSupport;

public class HelloServlet extends HttpServlet {

	private static final long serialVersionUID = 1L;
	private String greeting = "Hello World!";

	public HelloServlet() {
	}

	public HelloServlet(String greeting) {
		this.greeting = greeting;
	}

	/* (non-Javadoc)
	 * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
	 * 挂起,之后doGet会被重新调用一次,内容会被写出两遍,即doGet执行两次,至此请求结束;
	 * 但不知为何jetty又自动触发了一次target为favicon.ico的请求,doGet又两次被触发,但没有内容输出;
	 * 原因是:浏览器自动请求的,favicon.ico;把servlet对应的地址修改,不请求根地址就不会触发
	 */
	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		System.out.println("Start----"+System.currentTimeMillis());
		Continuation cc = ContinuationSupport.getContinuation(request);
		response.setContentType("text/html");
		response.setStatus(HttpServletResponse.SC_OK);
		response.getWriter().println("<h1>" + greeting + "</h1>");		
		response.getWriter().println("<h1>" + "pan" + "</h1>");
		if(cc.isInitial()){
			System.out.println("Suspend----"+System.currentTimeMillis());
			/*
			 * 要实现真正的comet,应该在产生向请求者发送的消息时,resume该请求将消息发送出去
			 * timeout作为长时间没有消息时的超时设定,心跳
			 * 在用户请求未进来时,发送的待推送消息,应该堆积等待请求到达并发送出去
			 */
    		cc.setTimeout(5000);
    		cc.suspend();		
		}
		System.out.println("After suspend----"+System.currentTimeMillis());
		response.getWriter().println(
				"session=" + request.getSession(true).getId());
//		response.getWriter().println("<h1>" + "zhexin" + "</h1>");
		response.getWriter().flush();//加flush内容一部分先被刷出,否则请求完成时,一起刷出
		try {
			if(cc.isSuspended()){//模拟消息到达,触发resume
				Thread.sleep(1000);
				System.out.println("before resume----"+System.currentTimeMillis());
				cc.resume();
				System.out.println("after resume----"+System.currentTimeMillis());
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	//Process the HTTP Post request
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws
            ServletException, IOException {
		System.out.println("doPost----"+System.currentTimeMillis());
        doGet(request, response);
    }
}


package cn.restful.jetty;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
  
/**    
 * @Title: TestServletServer.java 
 * @Package com.ddz.web.test 
 * @Description: TODO(jetty嵌入式容器,并验证了下jetty continucation实现comet的方式) 
 * @author Potter 
 * @date 2013-12-13 上午11:32:27 
 * @version V2.0    
 */  
public class TestServletServer {  
      
    public static void main(String[] args) throws Exception{  
        Server server=new Server(8080);  
        //第一种方式:通过构造函数,设置默认的session  
        ServletContextHandler context=new ServletContextHandler(ServletContextHandler.SESSIONS);  
        context.setContextPath("/");  
          
        server.setHandler(context);  
          
        //第二种方式:设置session  
        //Create the SessionHandler (wrapper) to handle the sessions  
        /*HashSessionManager manager = new HashSessionManager(); 
        SessionHandler sessions = new SessionHandler(manager); 
        context.setHandler(sessions);*/  
          
        context.addServlet(new ServletHolder(new HelloServlet()), "/test/*");  
          
        server.start();  
        server.join();  
    }  
  
}  
  • 学习笔记-Comet
            
    
    博客分类: 学习笔记 Comet长链接服务器推 
  • 大小: 16.7 KB
  • 学习笔记-Comet
            
    
    博客分类: 学习笔记 Comet长链接服务器推 
  • 大小: 11.4 KB