1. 程式人生 > >springboot搭建WebSocket服務端

springboot搭建WebSocket服務端

基於springboot框架編寫一個WebSocket服務端,並通過簡單的html介面模擬客戶端驗證服務端連線。

1、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.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <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>

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

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2、application.yml

spring:
  thymeleaf:
    prefix: classpath:/templates/

3、Server端程式碼

3.1、ServerEndpointExporter

首先要注入ServerEndpointExporter,這個bean會自動註冊使用了@ServerEndpoint註解宣告的Websocket endpoint。要注意,如果使用獨立的servlet容器,而不是直接使用springboot的內建容器,就不要注入ServerEndpointExporter,因為它將由容器自己提供和管理。

package com.example.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

}

3.2 WebSocketServer

package com.example.demo.webSocket;


import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/webSocketServer")
@Component
public class WebSocketDemo {


    @OnOpen
    public void onOpen(Session session) {
        System.out.println("新開啟了一個webSocket連線" + session.getId());
    }

    @OnMessage
    public String onMessage(String message, Session session) {
        System.out.println("收到客戶端傳送的資訊:"+message);
        System.out.println("當前的sessionId:"+session.getId());
        return "SUCCESS";
    }

    @OnClose
    public void onClose(Session session, CloseReason reason) {
        System.out.println("webSocket連線關閉:sessionId:"+session.getId() + "關閉原因是:"+reason.getReasonPhrase() + "code:"+reason.getCloseCode());
    }


    @OnError
    public void onError(Throwable t) {
        t.printStackTrace();
    }

}

4、前端html介面

<!DOCTYPE html>
<html>

<head>
    <meta name="viewport" content="width=device-width" />
    <title>WebSocket 客戶端</title>
</head>

<body>
<div>
    <input type="button" id="btnConnection" value="連線" />
    <input type="button" id="btnClose" value="關閉" />
    <input type="button" id="btnSend" value="傳送" />
</div>
<script src="/jquery-3.3.1.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
    $(function(){
        var socket;
        if(typeof(WebSocket) == "undefined") {
            alert("您的瀏覽器不支援WebSocket");
            return;
        }
        $("#btnConnection").click(function() {
            //實現化WebSocket物件,指定要連線的伺服器地址與埠
            socket = new WebSocket("ws://localhost:8080/webSocketServer");
            //開啟事件
            socket.onopen = function() {
                alert("Socket 已開啟");
                //socket.send("這是來自客戶端的訊息" + location.href + new Date());
            };
            //獲得訊息事件
            socket.onmessage = function(msg) {
                alert(msg.data);
            };
            //關閉事件
            socket.onclose = function() {
                alert("Socket已關閉");
            };
            //發生了錯誤事件
            socket.onerror = function() {
                alert("發生了錯誤");
            }
        });

        //傳送訊息
        $("#btnSend").click(function() {
            socket.send("這是來自客戶端的訊息" + location.href + new Date());
        });

        //關閉
        $("#btnClose").click(function() {
            socket.close();
        });
    });
</script>
</body>

</html>

5、跳轉介面程式碼

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HelloWorldController {
    @GetMapping("/hello")
    public String hello() {
        return "/webSocketDemo";
    }
}

6、目錄結構

7、當需要在websocket服務端注入Service或者Dao進行其他的業務邏輯時,常規注入是會產生空指標異常的。

我們需要在websocket中注入applilcationContext物件,從上下文物件中獲取。

方法如下:

7.1 先在main方法啟動時將application物件注入到webSocket物件中

public static void main(String[] args) {
    ConfigurableApplicationContext applicationContext = SpringApplication.run(RangerSocketApplication.class, args);
    GatewayWebSocket.setApplicationContext(applicationContext);
}

7.2 在webSocket服務端設定注入方法,並基於上下文進行獲取

private static ApplicationContext applicationContext;

public static void setApplicationContext(ApplicationContext context) {
    applicationContext = context;
}

7.3 在獲取時賦值

private GatewayDao gatewayDao;

if(gatewayDao == null) {
    gatewayDao = applicationContext.getBean(GatewayDao.class);
}