使用 tomcat8 開發 WebSocket 服務端很是簡單,大體有以下兩種方式。html
一、使用註解方式開發,被 @ServerEndpoint 修飾的 Java 類便可做爲 WebSocket 服務端java
二、繼承 Endpoint 基類實現 WebSocket 服務端web
開發被 @ServerEndpoint 修飾的類以後,該類中還能夠定義以下方法。api
被 @OnOpen 修飾的方法:當客戶端與該 WebSocket 服務端創建鏈接時激發該方法瀏覽器
被 @OnClose 修飾的方法:當客戶端與該 WebSocket 服務端斷開鏈接時激發該方法tomcat
被 @OnMessage 修飾的方法:當 WebSocket 服務端收到客戶端消息時激發該方法服務器
被 @OnError 修飾的方法:當客戶端與該 WebSocket 服務端鏈接出現錯誤時激發該方法。websocket
下面將基於 WebSocket 開發一個多人實時聊天的程序,該程序思路很簡單 -- 在這個程序中,每一個客戶所用的瀏覽器都與服務器創建一個 WebSocket,從而保持實時鏈接,這樣客戶端的瀏覽器能夠隨時把數據發送到服務器端;當服務器收到任何一個瀏覽器發送來的消息以後,將該消息依次向每一個客戶端瀏覽器發送一遍。session
按以下步驟開發 WebSocket 服務端程序便可app
一、定義 @OnOpen 修飾的方法,每當客戶端鏈接進來時激發該方法,程序使用集合保存全部鏈接進來的客戶端
二、定義 @OnMessage 修飾的方法,每當該服務端收到客戶端消息時激發該方法,服務端收到消息以後遍歷保存客戶端的集合,並將消息逐個發給全部客戶端
三、定義 @OnClose 修飾的方法,每當客戶端斷開與該服務端鏈接時激發該方法,程序將該客戶端從集合中刪除。
ChatEndpoint.java
package com.baiguiren; import java.io.IOException; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicInteger; import javax.websocket.OnClose; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.OnError; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; @ServerEndpoint(value="/websocket/chat") public class ChatEndpoint { private static final String GUEST_PREFIX = "訪客"; private static final AtomicInteger connectionIds = new AtomicInteger(0); // 定義一個集合,用於保存全部接入的 WebSocket 客戶端 private static final Set<ChatEndpoint> clientSet = new CopyOnWriteArraySet<>(); // 定義一個成員變量,記錄 WebSocket 客戶端的聊天暱稱 private final String nickname; // 定義一個成員變量,記錄與 WebSocket 之間的會話 private Session session; public ChatEndpoint() { nickname = GUEST_PREFIX + connectionIds.getAndIncrement(); } // 當客戶端鏈接進來時自動激發該方法 @OnOpen public void start(Session session) { this.session = session; // 將 WebSocket 客戶端會話添加到集合中 clientSet.add(this); String message = String.format("[%s %s]", nickname, "加入了聊天室"); // 發送消息 broadcast(message); } // 當客戶端斷開鏈接時自動激發該方法 @OnClose public void end() { clientSet.remove(this); String message = String.format("[%s %s]", nickname, "離開了聊天室!"); // 發送消息 broadcast(message); } // 每當收到客戶端消息時自動激發該方法 @OnMessage public void incoming(String message) { String filteredMessage = String.format("%s: %s", nickname, filter(message)); // 發送消息 broadcast(filteredMessage); } // 當客戶端通訊出現錯誤時激發該方法 @OnError public void onError(Throwable t) throws Throwable { System.out.println("WebSocket 服務端錯誤" + t); } // 實現廣播消息的工具方法 private static void broadcast(String msg) { // 遍歷服務器關聯的全部客戶端 for (ChatEndpoint client : clientSet) { try { synchronized (client) { // 發送消息 client.session.getBasicRemote().sendText(msg); } } catch (IOException e) { System.out.println("聊天錯誤,向客戶端" + client + "發送消息出現錯誤。"); clientSet.remove(client); try { client.session.close(); } catch (IOException el) {} String message = String.format("[%s %s]", client.nickname, "已經被斷開了鏈接"); broadcast(message); } } } // 定義一個工具方法,用於對字符串中的 HTML 字符標籤進行轉義 private static String filter(String message) { if (message == null) return null; char content[] = new char[message.length()]; message.getChars(0, message.length(), content, 0); StringBuilder result = new StringBuilder(content.length + 50); for (int i = 0; i < content.length; i++) { // 控制對尖括號等特殊字符轉義 switch (content[i]) { case '<': result.append("<"); break; case '>': result.append(">"); break; case '&': result.append("&"); break; case '"': result.append("""); break; default: result.append(content[i]); } } return (result.toString()); } }
以上文件須要導入 javaee-api-7.0.jar
須要說明的是,該 CharEndpoint 類並非真正的 WebSocket 服務端,它只實現了 WebSocket 服務端的核心功能,Tomcat 會調用它的方法做爲 WebSocket 服務端。所以,Tomcat 會爲每一個 WebSocket 客戶端建立一個 ChatEndpoint 對象,也就是說,有一個 WebSocket 服務端,程序就有一個 ChatEndpoint 對象。因此上面程序中的 clientSet 集合保存了多個 ChatEndpoint 對象,其中每一個 ChatEndpoint 對象對應一個 WebSocket 客戶端。
chat.html
<html> <head> <title>使用 WebSocket 通訊</title> </head> <body> <div style="width: 600px; height:240px;overflow-y: auto;border: 1px solid #333;" id="show"> </div> <input type="text" size="80" id="msg" name="msg" placeholder="請輸入聊天內容"/ > <input type="button" value="發送" id="sendBtn" name="sendBtn" /> <script> window.onload = function() { // 建立 WebSocket 對象 var webSocket = new WebSocket("ws://127.0.0.1:8080/jsp/websocket/chat"); var sendMsg = function() { var inputElement = document.getElementById('msg'); // 發送消息 webSocket.send(inputElement.value); // 清空單行文本框 inputElement.value = ""; }; var send = function(event) { if (event.keyCode == 13) { sendMsg(); } }; webSocket.onopen = function() { // 爲 onmessage 事件綁定監聽器,接收消息 webSocket.onmessage = function(event) { var show = document.getElementById('show'); // 接收並顯示消息 show.innerHTML += event.data + "<br/>"; show.scrollTop = show.scrollHeight; }; document.getElementById('msg').onkeydown = send; document.getElementById('sendBtn').onclick = sendMsg; }; webSocket.onclose = function() { // document.getElementById('msg').onkeydown = null; // document.getElementById('sendBtn').onclick = null; console.log('WebSocket已經被關閉'); }; } </script> </body> </html>