基于AJAX的长轮询(long-polling)方式实现COMET例子

什么是 Comet?

解释: Alex Russell ( Dojo Toolkit 的项目 Lead )称这种基于 HTTP 长连接、无须在浏览器端安装插件的 “ 服务器推 ” 技术为 “Comet” 。

有两种实现 Comet 应用的实现模型,目前主要讨论的是基于 AJAX 的长轮询 (long-polling) 方式

例子如下:

Servlet实现类:TestComet


 

Java代码   收藏代码
  1. <span style="font-family: tahoma,arial,helvetica,sans-serif; font-size: small;">public class TestComet extends HttpServlet implements CometProcessor {  
  2.       
  3.     private static final long serialVersionUID = 1L;  
  4.   
  5.     // 发送器  
  6.     private MessageSender messageSender = null;  
  7.   
  8.     private static final Integer TIMEOUT = 60 * 1000;  
  9.   
  10.     @Override  
  11.     public void destroy() {  
  12.         messageSender.stop();  
  13.         messageSender = null;  
  14.   
  15.     }  
  16.   
  17.     @Override  
  18.     public void init() throws ServletException {  
  19.         System.out.println("--init-----------------");  
  20.           
  21.         // 初始化发送器  
  22.         messageSender = new MessageSender();  
  23.         Thread messageSenderThread = new Thread(messageSender, "MessageSender["  
  24.                 + getServletContext().getContextPath() + "]");  
  25.         messageSenderThread.setDaemon(true);  
  26.           
  27.         // 启动发送线程  
  28.         messageSenderThread.start();  
  29.     }  
  30.   
  31.     public void event(final CometEvent event) throws IOException,  
  32.             ServletException {  
  33.         System.out.println("--event-----------------");  
  34.           
  35.         // 获取事件对应的REQUEST 和 RESPONSE  
  36.         HttpServletRequest request = event.getHttpServletRequest();  
  37.         HttpServletResponse response = event.getHttpServletResponse();  
  38.           
  39.           
  40.         if (event.getEventType() == CometEvent.EventType.BEGIN) {  
  41.             request.setAttribute("org.apache.tomcat.comet.timeout", TIMEOUT);  
  42.             log("Begin for session: " + request.getSession(true).getId());  
  43.             // 注入RESPONSE  
  44.             messageSender.setConnection(response);  
  45.               
  46.             Weatherman weatherman = new Weatherman(messageSender, 9511832408);  
  47.             new Thread(weatherman).start();  
  48.               
  49.         } else if (event.getEventType() == CometEvent.EventType.ERROR) {  
  50.             log("Error for session: " + request.getSession(true).getId());  
  51.             event.close();  
  52.         } else if (event.getEventType() == CometEvent.EventType.END) {  
  53.             log("End for session: " + request.getSession(true).getId());  
  54.             event.close();  
  55.         } else if (event.getEventType() == CometEvent.EventType.READ) {  
  56.             throw new UnsupportedOperationException(  
  57.                     "This servlet does not accept data");  
  58.         }  
  59.     }  
  60. }  
  61. </span>  

 信息发送器:

Java代码   收藏代码
  1. <span style="font-family: tahoma,arial,helvetica,sans-serif; font-size: small;">public class MessageSender implements Runnable {  
  2.   
  3.     // 标志位  
  4.     protected boolean running = true;  
  5.   
  6.     // 信息列表  
  7.     protected final ArrayList<String> messages = new ArrayList<String>();  
  8.   
  9.     // HTTP RESPONSE  
  10.     private ServletResponse connection;  
  11.   
  12.     // 注入HTTP RESPONSE  
  13.     public synchronized void setConnection(ServletResponse connection) {  
  14.         this.connection = connection;  
  15.         notify();  
  16.     }  
  17.   
  18.     // 发送信息  
  19.     public void send(String message) {  
  20.         // 同步队列,加入发送信息  
  21.         synchronized (messages) {  
  22.             messages.add(message);  
  23.             log("Message added #messages=" + messages.size());  
  24.             // 唤醒  
  25.             messages.notify();  
  26.         }  
  27.     }  
  28.   
  29.     public void run() {  
  30.   
  31.         // 线程启动  
  32.         log("start");  
  33.         while (running) {  
  34.             if (messages.size() == 0) {  
  35.                 try {  
  36.                     synchronized (messages) {  
  37.                         log("MessageSender wait[空闲状态,线程等待]");  
  38.                         // 释放锁  
  39.                         messages.wait();  
  40.                     }  
  41.                 } catch (InterruptedException e) {  
  42.                     e.printStackTrace();  
  43.                     // Ignore  
  44.                 }  
  45.             }  
  46.             String[] pendingMessages = null;  
  47.             synchronized (messages) {  
  48.                   
  49.                 // 导出发送的信息至数组  
  50.                 pendingMessages = messages.toArray(new String[0]);  
  51.                 // 清空信息队列  
  52.                 messages.clear();  
  53.             }  
  54.             try {  
  55.                 if (connection == null) {  
  56.                     try {  
  57.                         synchronized (this) {  
  58.                             // 等待注入HTTP RESPONSE  
  59.                             wait();  
  60.                         }  
  61.                     } catch (InterruptedException e) {  
  62.                         // Ignore  
  63.                         e.printStackTrace();  
  64.                     }  
  65.                 }  
  66.                   
  67.                 // 输出流操作  
  68.                 OutputStream out = connection.getOutputStream();  
  69.                 for (int j = 0; j < pendingMessages.length; j++) {  
  70.                     final String forecast = pendingMessages[j] + "<br>";  
  71.                     out.write(forecast.getBytes());  
  72.                     out.flush();  
  73.                     connection.flushBuffer();  
  74.                     log("Writing[写入]:" + forecast);  
  75.                 }  
  76.             } catch (IOException e) {  
  77.                 log("IOExeption sending message", e);  
  78.             }  
  79.         }  
  80.     }  
  81.   
  82.     // 停止  
  83.     public void stop() {  
  84.         running = false;  
  85.     }  
  86.   
  87.     // 日志  
  88.     private void log(Object obj) {  
  89.         System.out.println(obj);  
  90.     }  
  91.   
  92.     // 日志  
  93.     private void log(Object obj, Throwable e) {  
  94.         System.out.println(obj);  
  95.         e.printStackTrace();  
  96.     }  
  97.   
  98. }  
  99. </span>  

 YAHOO天气预报:

Java代码   收藏代码
  1. <span style="font-family: tahoma,arial,helvetica,sans-serif; font-size: small;">public class Weatherman implements Runnable {  
  2.   
  3.     // 链接列表  
  4.     private final List<URL> zipCodes;  
  5.       
  6.     // YAHOO WEATHER  
  7.     private final String YAHOO_WEATHER = "http://weather.yahooapis.com/forecastrss?p=";  
  8.       
  9.     // 发送器  
  10.     private MessageSender messageSender;  
  11.   
  12.     public Weatherman(MessageSender messageSender, Integer... zips) {  
  13.         this.messageSender = messageSender;  
  14.         zipCodes = new ArrayList<URL>(zips.length);  
  15.         for (Integer zip : zips) {  
  16.             try {  
  17.                 // 添加具体链接  
  18.                 zipCodes.add(new URL(YAHOO_WEATHER + zip));  
  19.             } catch (Exception e) {  
  20.                 e.printStackTrace();  
  21.             }  
  22.         }  
  23.     }  
  24.   
  25.     public void run() {  
  26.         System.out.println("Weatherman run[天气预报员启动]");  
  27.         int i = 0;  
  28.         while (i >= 0) {  
  29.             int j = i % zipCodes.size();  
  30.             SyndFeedInput input = new SyndFeedInput();  
  31.             try {  
  32.                 SyndFeed feed = input.build(new InputStreamReader(zipCodes.get(  
  33.                         j).openStream()));  
  34.                 SyndEntry entry = (SyndEntry) feed.getEntries().get(0);  
  35.                   
  36.                 // 发送数据  
  37.                 messageSender.send(entryToHtml(entry));  
  38.                   
  39.                 // 线程休眠  
  40.                 Thread.sleep(10000L);  
  41.             } catch (Exception e) {  
  42.                 e.printStackTrace();  
  43.             }  
  44.             i++;  
  45.         }  
  46.     }  
  47.   
  48.     // 格式转换  
  49.     private String entryToHtml(SyndEntry entry) {  
  50.         StringBuilder html = new StringBuilder("<h2>");  
  51.         html.append(entry.getTitle());  
  52.         html.append("</h2>");  
  53.         html.append(entry.getDescription().getValue());  
  54.         return html.toString();  
  55.     }  
  56. }</span>  

 WEB.XML配置:

Xml代码   收藏代码
  1. <span style="font-family: tahoma,arial,helvetica,sans-serif; font-size: small;">    <servlet>  
  2.         <description>TestComet</description>  
  3.         <display-name>TestComet</display-name>  
  4.         <servlet-name>TestComet</servlet-name>  
  5.         <servlet-class>cn.test.TestComet</servlet-class>  
  6.     </servlet>  
  7.   
  8.     <servlet-mapping>  
  9.         <servlet-name>TestComet</servlet-name>  
  10.         <url-pattern>/TestComet</url-pattern>  
  11.     </servlet-mapping></span>  

 TOMCAT配置,NIO

Xml代码   收藏代码
  1. <span style="font-family: tahoma,arial,helvetica,sans-serif; font-size: small;"><Connector connectionTimeout="20000" port="8888" protocol="org.apache.coyote.http11.Http11NioProtocol" redirectPort="8443"/></span>  


 


 

参考文档:

Comet :基于 HTTP 长连接的 “ 服务器推 ” 技术

http://www.ibm.com/developerworks/cn/web/wa-lo-comet/

 

使用 Java 实现 Comet 风格的 Web 应用(一)

http://blog.csdn.net/ligaoyang/archive/2009/08/03/4402889.aspx

 

使用 Java 实现 Comet 风格的 Web 应用(二)

http://blog.csdn.net/ligaoyang/archive/2009/08/03/4402898.aspx


 

注:

以上例子只支持FIREFOX,不支持IE

可能会出现一些问题,例如有些包可能有冲突,需要在context.xml中添加

<Loader delegate="true" />


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值