Java長(zhǎng)輪詢(xún)和長(zhǎng)鏈接都是可以用于實(shí)現(xiàn)實(shí)時(shí)通信的技術(shù),它們都可以讓瀏覽器和服務(wù)器保持長(zhǎng)時(shí)間的連接,從而實(shí)現(xiàn)實(shí)時(shí)推送消息或數(shù)據(jù)的功能。
對(duì)于Java長(zhǎng)輪詢(xún)來(lái)說(shuō),它的實(shí)現(xiàn)原理是客戶(hù)端不斷向服務(wù)器發(fā)送請(qǐng)求,如果服務(wù)器沒(méi)有新的數(shù)據(jù)或消息需要推送,那么它會(huì)一直等待,直到服務(wù)器推送了新的消息或者達(dá)到了設(shè)定的超時(shí)時(shí)間,才會(huì)返回響應(yīng)。而如果服務(wù)器有新的消息需要推送,那么它會(huì)在返回響應(yīng)的同時(shí),將這些消息一并返回給客戶(hù)端。
public class LongPollingController { @RequestMapping(value = "/long-polling", method = RequestMethod.GET, produces = "text/html;charset=UTF-8") @ResponseBody public String longPolling(HttpServletRequest request, HttpServletResponse response) { // 設(shè)置請(qǐng)求超時(shí)時(shí)間 long timeout = 30 * 1000L; // 獲取客戶(hù)端的最近一次時(shí)間戳 long clientTimestamp = Long.parseLong(request.getParameter("timestamp")); // 判斷是否有新的消息需要推送 boolean hasNewMessage = checkNewMessage(clientTimestamp); // 如果有新的消息,立即返回 if (hasNewMessage) { return "New message"; } // 否則等待超時(shí)時(shí)間,并返回空響應(yīng) // 注意,這里會(huì)將線程掛起,占用服務(wù)器資源,如果客戶(hù)端的連接數(shù)較多,服務(wù)器可能會(huì)承受不了 long start = System.currentTimeMillis(); while (!hasNewMessage && System.currentTimeMillis() - start < timeout) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } hasNewMessage = checkNewMessage(clientTimestamp); } return ""; } private boolean checkNewMessage(long clientTimestamp) { // TODO: 判斷是否有新的消息需要推送 return false; } }
長(zhǎng)鏈接則采用了WebSocket協(xié)議,它通過(guò)在HTTP協(xié)議升級(jí)時(shí)請(qǐng)求升級(jí)到WebSocket協(xié)議來(lái)實(shí)現(xiàn)實(shí)時(shí)通信,相比于長(zhǎng)輪詢(xún),在傳輸數(shù)據(jù)量相同的情況下可以節(jié)省更多的網(wǎng)絡(luò)資源,并且能夠支持雙向?qū)崟r(shí)通信。
public class WebSocketHandler extends TextWebSocketHandler { private final List<WebSocketSession> sessions = new ArrayList<>(); @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { // 連接建立時(shí),將WebSocketSession保存起來(lái) sessions.add(session); } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { // 連接關(guān)閉時(shí),將WebSocketSession從保存列表中移除 sessions.remove(session); } @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { // 收到文本消息時(shí),將消息廣播給所有客戶(hù)端 for (WebSocketSession s : sessions) { if (s.isOpen()) { s.sendMessage(message); } } } }
不同于長(zhǎng)輪詢(xún)需要不斷發(fā)起請(qǐng)求,通過(guò)WebSocket協(xié)議建立的長(zhǎng)鏈接只需要連接一次,客戶(hù)端和服務(wù)器之間的連接就會(huì)一直保持,直到某一方斷開(kāi)連接為止。