1. 程式人生 > >spring boot +WebSocket 廣播式(一)

spring boot +WebSocket 廣播式(一)

WebSocket 為瀏覽器和伺服器端提供了雙工非同步通訊的功能,即瀏覽器可以向伺服器傳送訊息,伺服器也可以向瀏覽器傳送訊息。WebSocket 需要瀏覽器的支援,如IE 10+、Chrome 13+、Firefox 6+。

Websocket 是通過一個socket來實現雙工非同步通訊的能力。但是直接使用WebSocket協議開發程式顯得特別煩瑣,我門會使用它的子協議STOMP,它是一個更高階級別的協議,STOMP協議使用一個基於幀(frame)的格式來定義資訊,與HTTP的request 和response 類似(具有類似於 @RequestMapping 的 @MessageMapping )在後面的內容中可以觀察STOMP的frame

廣播式

即伺服器段友訊息時,會將訊息傳送給所有連結了當前endpoint 的瀏覽器。

也就是開啟多個瀏覽器tab。連結上websocket 以後一個tab傳送資訊,其他連結的tab 也能收到。

目錄結構:

這裡寫圖片描述

實現 後臺

1. 新建maven專案

pom.xml如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>com.us.example</groupId> <artifactId>springWebSocket</artifactId> <version>1.0-SNAPSHOT</version>
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.0.RELEASE</version> </parent> <properties> <start-class>com.us.WebApplication</start-class> <maven.compiler.target>1.8</maven.compiler.target> <maven.compiler.source>1.8</maven.compiler.source> </properties> <dependencies> <!-- 核心模組,包括自動配置支援、日誌和YAML --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!-- 測試模組,包括JUnit、Hamcrest、Mockito --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!-- 引入Web模組 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- SpringWebSocket依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> </dependencies> </project>

2.新建WebSocket 的配置類

WebSocketConfig.java

package com.us.example.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;

/**
 * Created by yangyibo on 16/12/29.
 */
@Configuration
@EnableWebSocketMessageBroker
//通過EnableWebSocketMessageBroker 開啟使用STOMP協議來傳輸基於代理(message broker)的訊息,此時瀏覽器支援使用@MessageMapping 就像支援@RequestMapping一樣。
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer{


    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) { //endPoint 註冊協議節點,並對映指定的URl

        registry.addEndpoint("/endpointWisely").withSockJS();//註冊一個Stomp 協議的endpoint,並指定 SockJS協議。
    }


    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {//配置訊息代理(message broker)
        registry.enableSimpleBroker("/topic"); //廣播式應配置一個/topic 訊息代理

    }
}

3.瀏覽器向伺服器傳送的訊息接收類

package com.us.example.bean;

/**
 * Created by yangyibo on 16/12/29.
 * 瀏覽器向伺服器傳送的訊息使用此類接受
 */
public class Message {
    private String name;

    public String getName(){
        return name;
    }
}

4.伺服器向瀏覽器傳送的此類訊息。

package com.us.example.bean;

/**
 * Created by yangyibo on 16/12/29.
 * 伺服器向瀏覽器傳送的此類訊息。
 */
public class Response {
    public void setResponseMessage(String responseMessage) {
        this.responseMessage = responseMessage;
    }

    private String responseMessage;
    public Response(String responseMessage){
        this.responseMessage = responseMessage;
    }
    public String getResponseMessage(){
        return responseMessage;
    }
}

5.演示控制器程式碼

package com.us.example.controller;



import com.us.example.bean.Message;
import com.us.example.bean.Response;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;

import org.springframework.stereotype.Controller;


/**
 * Created by yangyibo on 16/12/29.
 *
 */
@Controller
public class WebSocketController {

    @MessageMapping("/welcome")//瀏覽器傳送請求通過@messageMapping 對映/welcome 這個地址。
    @SendTo("/topic/getResponse")//伺服器端有訊息時,會訂閱@SendTo 中的路徑的瀏覽器傳送訊息。
    public Response say(Message message) throws Exception {
        Thread.sleep(1000);
        return new Response("Welcome, " + message.getName() + "!");
    }
  }

實現前臺

6. 新增指令碼

新增指令碼,將stomp.js、sockjs.min.js 以及jQuery 指令碼放在src/main/resources/static下。

7.演示頁面

在src/main/resources/templates 下新建 ws.html。(原始碼會在文章底部給出)

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8" />
    <title>Spring Boot+WebSocket+廣播式</title>

</head>
<body onload="disconnect()">
<noscript><h2 style="color: #ff0000">貌似你的瀏覽器不支援websocket</h2></noscript>
<div>
    <div>
        <button id="connect" onclick="connect();">連線</button>
        <button id="disconnect" disabled="disabled" onclick="disconnect();">斷開連線</button>
    </div>
    <div id="conversationDiv">
        <label>輸入你的名字</label><input type="text" id="name" />
        <button id="sendName" onclick="sendName();">傳送</button>
        <p id="response"></p>
    </div>
</div>
<script th:src="@{sockjs.min.js}"></script>
<script th:src="@{stomp.min.js}"></script>
<script th:src="@{jquery.js}"></script>
<script type="text/javascript">
    var stompClient = null;

    function setConnected(connected) {
        document.getElementById('connect').disabled = connected;
        document.getElementById('disconnect').disabled = !connected;
        document.getElementById('conversationDiv').style.visibility = connected ? 'visible' : 'hidden';
        $('#response').html();
    }

    function connect() {
        var socket = new SockJS('/endpointWisely'); //連結SockJS 的endpoint 名稱為"/endpointWisely"
        stompClient = Stomp.over(socket);//使用stomp子協議的WebSocket 客戶端
        stompClient.connect({}, function(frame) {//連結Web Socket的服務端。
            setConnected(true);
            console.log('Connected: ' + frame);
            stompClient.subscribe('/topic/getResponse', function(respnose){ //訂閱/topic/getResponse 目標傳送的訊息。這個是在控制器的@SendTo中定義的。
                showResponse(JSON.parse(respnose.body).responseMessage);
            });
        });
    }


    function disconnect() {
        if (stompClient != null) {
            stompClient.disconnect();
        }
        setConnected(false);
        console.log("Disconnected");
    }

    function sendName() {
        var name = $('#name').val();
        //通過stompClient.send 向/welcome 目標 傳送訊息,這個是在控制器的@messageMapping 中定義的。
        stompClient.send("/welcome", {}, JSON.stringify({ 'name': name }));
    }

    function showResponse(message) {
          var response = $("#response");
          response.html(message);
    }
</script>
</body>
</html>

8.配置view Controller

為ws提供便捷的路徑對映

package com.us.example.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
public class WebMvcConfig  extends WebMvcConfigurerAdapter{
    //為ws.HTML 提供便捷的路徑對映。
     @Override
       public void addViewControllers(ViewControllerRegistry registry) {
           registry.addViewController("/ws").setViewName("/ws");
           }
    }

9.啟動入口

package com.us.example;

/**
 * Created by yangyibo on 16/12/29.
 */
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

import static org.springframework.boot.SpringApplication.run;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        ConfigurableApplicationContext run = run(Application.class, args);
    }
}

本文參考:《JavaEE開發的顛覆者:Spring Boot實戰 》