SpringBoot | 第十九章:web應用開發之WebSocket

前言

web開發也講解了三章了,這章節開始講解關於與前端通訊相關知識。實現一個在線聊天室相似的功能或者後端推送消息到前端,在沒有WebSocket時,讀大學那夥還有接觸過DWR(Direct Web Remoting),也使用過輪詢的方式,當Servlet3.0出來後,也有使用其異步鏈接機制進行先後端通訊的。今天咱們就來講說WebSocket。它是HTML5開始提供的。html

關於WebSocket

WebSocketHTML5開始提供的一種在單個TCP鏈接上進行全雙工通信的協議。前端

WebSocket API中,瀏覽器和服務器只須要作一個握手的動做,而後,瀏覽器和服務器之間就造成了一條快速通道。二者之間就直接能夠數據互相傳送。html5

瀏覽器經過JavaScript向服務器發出創建WebSocket鏈接的請求,鏈接創建之後,客戶端和服務器端就能夠經過TCP鏈接直接交換數據。java

當獲取Web Socket鏈接後,你能夠經過 send() 方法來向服務器發送數據,並經過 onmessage 事件來接收服務器返回的數據。git

對於前端,建立一個WebSocket對象,以下:github

var Socket = new WebSocket(url, [protocol] );

說明:第一個參數 url, 指定鏈接的 URL。第二個參數 protocol 是可選的,指定了可接受的子協議。web

WebSocker屬性

如下是WebSocket對象的屬性。假定咱們使用了以上代碼建立了Socket對象:spring

屬性 描述
Socket.readyState 只讀屬性 readyState 表示鏈接狀態,能夠是如下值:<br>0 - 表示鏈接還沒有創建。<br> 1 - 表示鏈接已創建,能夠進行通訊。<br>2 - 表示鏈接正在進行關閉。<br>3 - 表示鏈接已經關閉或者鏈接不能打開。
Socket.bufferedAmount 只讀屬性 bufferedAmount 已被 send() 放入正在隊列中等待傳輸,可是尚未發出的 UTF-8 文本字節數。

WebSocket事件

如下是 WebSocket 對象的相關事件。假定咱們使用了以上代碼建立了 Socket 對象:後端

事件 事件處理程序 描述
open Socket.onopen 鏈接創建時觸發
message Socket.onmessage 客戶端接收服務端數據時觸發
error Socket.onerror 通訊發生錯誤時觸發
close Socket.onclose 鏈接關閉時觸發

WebSocket方法

如下是 WebSocket 對象的相關方法。假定咱們使用了以上代碼建立了 Socket 對象:瀏覽器

方法 描述
Socket.send() 使用鏈接發送數據
Socket.close() 關閉鏈接

WebSocket實踐

前面介紹了在瀏覽器端webSocket的相關知識點,如今咱們就來搭建一個後臺對接應用,以實現一個簡單的在線聊天室。

一點知識

後端關於WebSocket的實現是基於JSR356標準的。該標準的出現,統一了 WebSocket的代碼寫法。只要支持web容器支持JSR356標準,那麼實現方式是一致的。而目前實現方式有兩種,一種是註解方式,另外一種就是繼承繼承javax.websocket.Endpoint類了。

經常使用註解說明

  • @WebSocketEndpoint 註解是一個類層次的註解,它的功能主要是將目前的類定義成一個websocket服務器端。註解的值將被用於監聽用戶鏈接的終端訪問URL地址。

  • @onOpen 打開一個新鏈接,即有新鏈接時,會調用被此註解的方法。

  • @onClose 關閉鏈接時調用。

  • @onMessage 當服務器接收到客戶端發送的消息時所調用的方法。

  • @PathParam 接收uri參數的,與@PathVariable功能差很少,可經過url獲取對應值

搭建一個簡易聊天室

0.加入POM依賴。

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

1.編寫控制層,對應WebSocket的各事件。同時抽取了個公用類,進行通用方法調用。

WebSocketController.java

/**
 * websocket 簡易聊天
 * @author oKong
 *
 */
//因爲是websocket 因此本來是@RestController的http形式 
//直接替換成@ServerEndpoint便可,做用是同樣的 就是指定一個地址
//表示定義一個websocket的Server端
@Component
@ServerEndpoint(value = "/my-chat/{usernick}")
@Slf4j
public class WebSocketController {
    
    /**
     * 鏈接事件 加入註解
     * @param session
     */
    @OnOpen
    public void onOpen(@PathParam(value = "usernick") String userNick,Session session) {
        String message = "有新遊客[" + userNick + "]加入聊天室!";
        log.info(message);
        WebSocketUtil.addSession(userNick, session);    
        //此時可向全部的在線通知 某某某登陸了聊天室            
        WebSocketUtil.sendMessageForAll(message);
    }
    
    @OnClose
    public void onClose(@PathParam(value = "usernick") String userNick,Session session) {
        String message = "遊客[" + userNick + "]退出聊天室!";
        log.info(message);
        WebSocketUtil.remoteSession(userNick);    
        //此時可向全部的在線通知 某某某登陸了聊天室            
        WebSocketUtil.sendMessageForAll(message);
    }
    
    @OnMessage
    public void OnMessage(@PathParam(value = "usernick") String userNick, String message) {
        //相似羣發
        String info = "遊客[" + userNick + "]:" + message;
        log.info(info);
        WebSocketUtil.sendMessageForAll(message);
    } 
    
    @OnError
    public void onError(Session session, Throwable throwable) {
        log.error("異常:", throwable);
        try {
            session.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        throwable.printStackTrace();
    }

}

WebSocketUtil.java

public class WebSocketUtil {

    /**
     * 簡單使用map進行存儲在線的session
     * 
     */
    private static final Map<String, Session> ONLINE_SESSION = new ConcurrentHashMap<>();
    
    public static void addSession(String userNick,Session session) {
        //putIfAbsent 添加鍵—值對的時候,先判斷該鍵值對是否已經存在
        //不存在:新增,並返回null
        //存在:不覆蓋,直接返回已存在的值
//        ONLINE_SESSION.putIfAbsent(userNick, session);
        //簡單示例 不考慮複雜狀況。。怎麼簡單怎麼來了。。
        ONLINE_SESSION.put(userNick, session);
    }
    
    public static void remoteSession(String userNick) {
        ONLINE_SESSION.remove(userNick);
    }
    
    /**
     * 向某個用戶發送消息
     * @param session 某一用戶的session對象
     * @param message
     */
    public static void sendMessage(Session session, String message) {
        if(session == null) {
            return;
        }
        // getAsyncRemote()和getBasicRemote()異步與同步
        Async async = session.getAsyncRemote();
        //發送消息
        async.sendText(message);
    }
    
    /**
     * 向全部在線人發送消息
     * @param message
     */
    public static void sendMessageForAll(String message) {
        //jdk8 新方法
        ONLINE_SESSION.forEach((sessionId, session) -> sendMessage(session, message));
    }
}

注意點:

  1. @ServerEndpoint的value值填寫時,開頭須要加上/,否則會提示路徑無效。
  2. 須要加上類型@Component註解,使得能被掃描到。
  3. 這裏的session等,都在包**javax.websocket**包下的,注意區分。

2.編寫主啓動類,主要是加入註解@EnableWebSocket和申明一個Websocket endpoint類。

@SpringBootApplication
@EnableWebSocket
@Slf4j
public class Chapter19Application {

    public static void main(String[] args) {
        SpringApplication.run(Chapter19Application.class, args);
        log.info("Chapter19啓動!");
    }
    
    /**
     * 會自動註冊使用了@ServerEndpoint註解聲明的Websocket endpoint
     * 要注意,若是使用獨立的servlet容器,
     * 而不是直接使用springboot的內置容器,
     * 就不要注入ServerEndpointExporter,由於它將由容器本身提供和管理。
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

3.啓動應用,利用在線的測試工具進行測試。這裏直接使用了http://coolaf.com/tool/chattest進行測試。固然也能夠本身寫一個html了。

首先,輸入咱們的服務地址:ws://127.0.0.1:8080/my-chat/okong,鏈接後就能夠看見服務器返回的消息了。

加入

咱們再開一個標籤頁,而後繼續以另外一個身份進入:

新遊客加入

這時,能夠看見第一個頁面開的,也收到消息了。如今咱們發送一條消息:

發送消息

而後,其中一個斷開鏈接:

斷開鏈接

而後能夠愉快聊天了,簡單的一個聊天室就完成了。

參考資料

  1. https://docs.spring.io/spring/docs/4.3.18.RELEASE/spring-framework-reference/htmlsingle/#websocket
  2. https://docs.spring.io/spring-boot/docs/1.5.15.RELEASE/reference/htmlsingle/#boot-features-websockets
  3. http://www.oracle.com/technetwork/articles/java/jsr356-1937161.html
  4. http://www.runoob.com/html/html5-websocket.html

總結

本章節主要是講解了WebSocket的使用。由於有統一標準的存在,編寫webSocket也是很簡單的。對於如何一對一聊天,你們能夠自行編寫下,由於知道了對方名稱,就能找出對方的session而後就能發送消息了。

最後

目前互聯網上不少大佬都有SpringBoot系列教程,若有雷同,請多多包涵了。本文是做者在電腦前一字一句敲的,每一步都是本身實踐的。若文中有所錯誤之處,還望提出,謝謝。

老生常談

  • 我的QQ:499452441
  • 微信公衆號:lqdevOps

公衆號

我的博客:http://blog.lqdev.cn

完整示例:https://github.com/xie19900123/spring-boot-learning/tree/master/chapter-19

原文地址:http://blog.lqdev.cn/2018/08/14/springboot/chapter-nineteen/

相關文章
相關標籤/搜索