日期:2014-05-16  浏览次数:20917 次

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

什么是 Comet?

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

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

例子如下:

Servlet实现类:TestComet


?

public class TestComet extends HttpServlet implements CometProcessor {
	
	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");
		}
	}
}


?信息发送器:

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 Strin