基於AJAX的長輪詢(long-polling)方式實現COMET例子
阿新 • • 發佈:2019-01-28
什麼是 Comet?
解釋: Alex Russell ( Dojo Toolkit 的專案 Lead )稱這種基於 HTTP 長連線、無須在瀏覽器端安裝外掛的 “ 伺服器推 ” 技術為 “Comet” 。
有兩種實現 Comet 應用的實現模型,目前主要討論的是基於 AJAX 的長輪詢 (long-polling) 方式
例子如下:
Servlet實現類:TestComet
Java程式碼- <span style="font-family: tahoma,arial,helvetica,sans-serif; font-size: small;">public class
- private static final long serialVersionUID = 1L;
- // 傳送器
- private MessageSender messageSender = null;
- private static final Integer TIMEOUT = 60 * 1000;
- @Override
- public void destroy() {
- messageSender.stop();
- messageSender = null
- }
- @Override
- public void init() throws ServletException {
- System.out.println("--init-----------------");
- // 初始化傳送器
- 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 {
- System.out.println("--event-----------------");
- // 獲取事件對應的REQUEST 和 RESPONSE
- HttpServletRequest request = event.getHttpServletRequest();
- HttpServletResponse response = event.getHttpServletResponse();
- if (event.getEventType() == CometEvent.EventType.BEGIN) {
- request.setAttribute("org.apache.tomcat.comet.timeout", TIMEOUT);
- log("Begin for session: " + request.getSession(true).getId());
- // 注入RESPONSE
- messageSender.setConnection(response);
- Weatherman weatherman = new Weatherman(messageSender, 95118, 32408);
- new Thread(weatherman).start();
- } else if (event.getEventType() == CometEvent.EventType.ERROR) {
- log("Error for session: " + request.getSession(true).getId());
- event.close();
- } else if (event.getEventType() == CometEvent.EventType.END) {
- log("End for session: " + request.getSession(true).getId());
- event.close();
- } else if (event.getEventType() == CometEvent.EventType.READ) {
- throw new UnsupportedOperationException(
- "This servlet does not accept data");
- }
- }
- }
- </span>
資訊傳送器:
Java程式碼- <span style="font-family: tahoma,arial,helvetica,sans-serif; font-size: small;">public class MessageSender implements Runnable {
- // 標誌位
- protected boolean running = true;
- // 資訊列表
- protected final ArrayList<String> messages = new ArrayList<String>();
- // HTTP RESPONSE
- private ServletResponse connection;
- // 注入HTTP RESPONSE
- public synchronized void setConnection(ServletResponse connection) {
- this.connection = connection;
- notify();
- }
- // 傳送資訊
- public void send(String message) {
- // 同步佇列,加入傳送資訊
- synchronized (messages) {
- messages.add(message);
- log("Message added #messages=" + messages.size());
- // 喚醒
- messages.notify();
- }
- }
- public void run() {
- // 執行緒啟動
- log("start");
- while (running) {
- if (messages.size() == 0) {
- try {
- synchronized (messages) {
- log("MessageSender wait[空閒狀態,執行緒等待]");
- // 釋放鎖
- messages.wait();
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- // Ignore
- }
- }
- String[] pendingMessages = null;
- synchronized (messages) {
- // 匯出傳送的資訊至陣列
- pendingMessages = messages.toArray(new String[0]);
- // 清空資訊佇列
- messages.clear();
- }
- try {
- if (connection == null) {
- try {
- synchronized (this) {
- // 等待注入HTTP RESPONSE
- wait();
- }
- } catch (InterruptedException e) {
- // Ignore
- e.printStackTrace();
- }
- }
- // 輸出流操作
- OutputStream out = connection.getOutputStream();
- for (int j = 0; j < pendingMessages.length; j++) {
- final String forecast = pendingMessages[j] + "<br>";
- out.write(forecast.getBytes());
- out.flush();
- connection.flushBuffer();
- log("Writing[寫入]:" + forecast);
- }
- } catch (IOException e) {
- log("IOExeption sending message", e);
- }
- }
- }
- // 停止
- public void stop() {
- running = false;
- }
- // 日誌
- private void log(Object obj) {
- System.out.println(obj);
- }
- // 日誌
- private void log(Object obj, Throwable e) {
- System.out.println(obj);
- e.printStackTrace();
- }
- }
- </span>
YAHOO天氣預報:
Java程式碼- <span style="font-family: tahoma,arial,helvetica,sans-serif; font-size: small;">public class Weatherman implements Runnable {
- // 連結列表
- private final List<URL> zipCodes;
- // YAHOO WEATHER
- private final String YAHOO_WEATHER = "http://weather.yahooapis.com/forecastrss?p=";
- // 傳送器
- private MessageSender messageSender;
- public Weatherman(MessageSender messageSender, Integer... zips) {
- this.messageSender = messageSender;
- zipCodes = new ArrayList<URL>(zips.length);
- for (Integer zip : zips) {
- try {
- // 新增具體連結
- zipCodes.add(new URL(YAHOO_WEATHER + zip));
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- public void run() {
- System.out.println("Weatherman run[天氣預報員啟動]");
- int i = 0;
- while (i >= 0) {
- int j = i % zipCodes.size();
- SyndFeedInput input = new SyndFeedInput();
- try {
- SyndFeed feed = input.build(new InputStreamReader(zipCodes.get(
- j).openStream()));
- SyndEntry entry = (SyndEntry) feed.getEntries().get(0);
- // 傳送資料
- messageSender.send(entryToHtml(entry));
- // 執行緒休眠
- Thread.sleep(10000L);
- } catch (Exception e) {
- e.printStackTrace();
- }
- i++;
- }
- }
- // 格式轉換
- private String entryToHtml(SyndEntry entry) {
- StringBuilder html = new StringBuilder("<h2>");
- html.append(entry.getTitle());
- html.append("</h2>");
- html.append(entry.getDescription().getValue());
- return html.toString();
- }
- }</span>
WEB.XML配置:
Xml程式碼- <span style="font-family: tahoma,arial,helvetica,sans-serif; font-size: small;"> <servlet>
- <description>TestComet</description>
- <display-name>TestComet</display-name>
- <servlet-name>TestComet</servlet-name>
- <servlet-class>cn.test.TestComet</servlet-class>
- </servlet>
- <servlet-mapping>
- <servlet-name>TestComet</servlet-name>
- <url-pattern>/TestComet</url-pattern>
- </servlet-mapping></span>
TOMCAT配置,NIO
Xml程式碼- <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 長連線的 “ 伺服器推 ” 技術
使用 Java 實現 Comet 風格的 Web 應用(一)
使用 Java 實現 Comet 風格的 Web 應用(二)
注:
以上例子只支援FIREFOX,不支援IE
可能會出現一些問題,例如有些包可能有衝突,需要在context.xml中新增
<Loader delegate="true" />