1. 程式人生 > >Servlet3.0 非同步處理 頁面推送 Comet 例項

Servlet3.0 非同步處理 頁面推送 Comet 例項

本例參考:http://blog.csdn.net/chenxiang0207/article/details/14054681/

我按照上面博文的思路重新走了一遍

專案結構如下圖


/**
 * AsyncServlet
 * 
 * 支援非同步處理的Servlet
 * 頁面中隱藏的iframe通過訪問此Servlet來建立HTTP長連線
 * 從而後臺能實時的推送javascript程式碼給頁面呼叫
 *
 */
@WebServlet(urlPatterns = "/Async", asyncSupported = true)
public class AsyncServlet extends HttpServlet {

	private static final long serialVersionUID = 822178713133426493L;
	private final static int DEFAULT_TIME_OUT = 10 * 60 * 1000;

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse res) {
		AsyncContext actx = req.startAsync();
		actx.setTimeout(DEFAULT_TIME_OUT);
		actx.addListener(new AsyncListener() {

			@Override
			public void onComplete(AsyncEvent arg0) throws IOException {
				// TODO Auto-generated method stub
				ClientComet.getInstance().removeAsyncContext(actx);
				System.out.println("AsyncListener-->onComplete");
			}

			@Override
			public void onError(AsyncEvent arg0) throws IOException {
				// TODO Auto-generated method stub
				ClientComet.getInstance().removeAsyncContext(actx);
				System.out.println("AsyncListener-->onError");
			}

			@Override
			public void onStartAsync(AsyncEvent arg0) throws IOException {
				// TODO Auto-generated method stub
				System.out.println("AsyncListener-->onStartAsync");
			}

			@Override
			public void onTimeout(AsyncEvent arg0) throws IOException {
				// TODO Auto-generated method stub
				ClientComet.getInstance().removeAsyncContext(actx);
				System.out.println("AsyncListener-->onTimeout");
			}

		});
		ClientComet.getInstance().addAsyncContext(actx);
	}
}

/**
 * ClientComet
 * 
 * 管理使用者的AsyncContext(新增、刪除)
 * 
 * 通過開啟一個執行緒來不斷地從mesgQueue獲取javascript 並遍歷使用者的AsyncContext來吧javascript推送給每個使用者
 *
 */
public class ClientComet {
	private static ClientComet instance;
	private ConcurrentLinkedQueue<AsyncContext> actxQueue;
	private LinkedBlockingQueue<Javascript> mesgQueue;

	private ClientComet() {
		actxQueue = new ConcurrentLinkedQueue<AsyncContext>();
		mesgQueue = new LinkedBlockingQueue<Javascript>();
		new ClientCometThread().start();
	}

	public static ClientComet getInstance() {
		if (instance == null) {
			instance = new ClientComet();
		}
		return instance;
	}

	public void addAsyncContext(AsyncContext actx) {
		actxQueue.add(actx);
	}

	public void removeAsyncContext(AsyncContext actx) {
		actxQueue.remove();
	}

	public void callClient(Javascript javascript) {
		mesgQueue.add(javascript);
	}

	protected class ClientCometThread extends Thread {

		@Override
		public void run() {
			while (true) {
				try {
					Javascript javascript = mesgQueue.take();
					for (AsyncContext actx : actxQueue) {
						PrintWriter writer = actx.getResponse().getWriter();
						writer.write(javascript.getScript());
						writer.flush();
						System.out
								.println("ClientCometThread-->sendJavaScript");
					}
				} catch (InterruptedException | IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

			}
		}
	}

}

/**
 * Javascript
 *
 * 提供將javascript的方法呼叫補全到一個script標籤中的功能
 */
public class Javascript {
	private String script;

	public Javascript(String func) {
		script = "<script type='text/javascript'>" + "\n" + "window.parent."
				+ func + "\n" + "</script>" + "\n";
	}

	public String getScript() {
		return script;
	}
}

/**
 * TestServlet
 * 
 * 訪問此Servlet能夠給ClientComet的mesgQueue新增物件
 *
 */
@WebServlet(urlPatterns = "/Test")
public class TestServlet extends HttpServlet {

	private static final long serialVersionUID = -7817902387051107187L;

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse res) {
		int times = 5;
		while (--times >= 0) {
			try {
				Thread.sleep(5000);
				ClientComet.getInstance().callClient(
						new Javascript("append(" + "\'"
								+ new Date().toGMTString() + "\'" + ")"));
				System.out.println("TestServlet-->callClient");
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

Comet.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"
	pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Comet Test</title>
<script type='text/javascript'>
	function append(str) {
		var textField = document.getElementById("textField");
		textField.innerHTML = textField.innerHTML + str + "<br/>";
	};
</script>
</head>
<body>
	<!-- textField——用來顯示伺服器推送的內容 -->
	<p id="textField"></p>
	<!-- cometFrame——隱藏的iframe,用來訪問AsyncServlet,建立長連線(注意,這樣做window.onload的函式將失效,或直到此連線斷開才執行) -->
	<iframe id="cometFrame" style="display: none;" src="/CometTest/Async"></iframe>
</body>
</html>


開啟Tomcat

開啟一個頁面A先訪問http://localhost:8080/CometTest/Comet.jsp

再開啟一個新的頁面B訪問http://localhost:8080/CometTest/Test

此時能夠看到A的內容不斷的增加

PS:若想讓onload函式不失效可以把對iframe的src的賦值操作放在onload中,而一開始src為空

專案打包:http://download.csdn.net/detail/u010497228/8415545