tomcat8 的 websocket 支持

使用 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>
相關文章
相關標籤/搜索