springboot整合websocket實現消息推送


​最近想起之前項目里面的一個實現,是關於訂閱推送的,當粉絲訂閱了大V或者說作者發布的內容被評論和點贊之后,對應的用戶會受到通知,當然,本身系統用戶並不多,所以直接采用的是輪訓的方式,由前端這邊定時向后端發起接口請求,獲取消息推送,無疑呢,此種方式也可以解決問題,但是大部分請求基本無用,白白浪費帶寬和網絡資源。

今天難得媳婦兒帶孩子回娘家了,下班到家也無事,便想着整理下前后端通過websocket實現消息推送的方式。當然,前端這塊,主要采用原始的js通過websocket的方式獲取消息,在微信公眾號和支付寶小程序開發中都有相應的onWebSocekt方式,有興趣的同學可以自行學習。

廢話不多說,開始啃代碼。

1、pom.xml

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


        <!--thymeleaf-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

 

 

2、application.yml

server:
  port: 8080


spring:
  thymeleaf:
    cache: false # 開發時關閉緩存,不然沒法看到實時頁面
    mode: HTML # 用非嚴格的 HTML
    encoding: UTF-8
    servlet:
      content-type: text/html

 

3、WebSocketServer,實現前后端的長連接

package com.cookie.server;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Author : cxq
 * @Date : 2020/8/31 15:50
 */

// 前端通過該連接與后端保持交互
@ServerEndpoint(value = "/server")
@Component
public class WebSocketServer {

    @PostConstruct
    public void init() {
        System.out.println("websocket 加載");
    }
    private static Logger log = LoggerFactory.getLogger(WebSocketServer.class);
    private static final AtomicInteger OnlineCount = new AtomicInteger(0);
    // concurrent包的線程安全Set,用來存放每個客戶端對應的Session對象。
    private static CopyOnWriteArraySet<Session> SessionSet = new CopyOnWriteArraySet<Session>();


    /**
     * 連接建立成功調用的方法
     */
    @OnOpen
    public void onOpen(Session session) {
        SessionSet.add(session);
        int cnt = OnlineCount.incrementAndGet(); // 在線數加1
        log.info("有連接加入,當前連接數為:{}", cnt);
        SendMessage(session, "連接成功");
    }

    /**
     * 連接關閉調用的方法
     */
    @OnClose
    public void onClose(Session session) {
        SessionSet.remove(session);
        int cnt = OnlineCount.decrementAndGet();
        log.info("有連接關閉,當前連接數為:{}", cnt);
    }

    /**
     * 收到客戶端消息后調用的方法
     *
     * @param message
     *            客戶端發送過來的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("來自客戶端的消息:{}",message);
        SendMessage(session, "收到消息,消息內容:"+message);

    }

    /**
     * 出現錯誤
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("發生錯誤:{},Session ID: {}",error.getMessage(),session.getId());
        error.printStackTrace();
    }

    /**
     * 發送消息,實踐表明,每次瀏覽器刷新,session會發生變化。
     * @param session
     * @param message
     */
    public static void SendMessage(Session session, String message) {
        try {
//            session.getBasicRemote().sendText(String.format("%s (From Server,Session ID=%s)",message,session.getId()));
            session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            log.error("發送消息出錯:{}", e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 群發消息
     * @param message
     * @throws IOException
     */
    public static void BroadCastInfo(String message) throws IOException {
        for (Session session : SessionSet) {
            if(session.isOpen()){
                SendMessage(session, message);
            }
        }
    }

    /**
     * 指定Session發送消息
     * @param sessionId
     * @param message
     * @throws IOException
     */
    public static void SendMessage(String message,String sessionId) throws IOException {
        Session session = null;
        for (Session s : SessionSet) {
            if(s.getId().equals(sessionId)){
                session = s;
                break;
            }
        }
        if(session!=null){
            SendMessage(session, message);
        }
        else{
            log.warn("沒有找到你指定ID的會話:{}",sessionId);
        }
    }
}

 

 

4、WebSocketController,主要實現消息群發和一對一發送

package com.cookie.controller;

import com.cookie.server.WebSocketServer;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

/**
 * @Author : cxq
 * @Date : 2020/8/31 16:19
 */

@RestController
@RequestMapping("/webSocket")
public class WebSocketController {


    /**
     * 群發消息內容
     *
     * @param message
     * @return
     */
    @RequestMapping(value = "/sendAll", method = RequestMethod.GET)
    public String sendAllMessage(@RequestParam(required = true) String message) {
        try {
            WebSocketServer.BroadCastInfo(message);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "ok";
    }

    /**
     * 指定會話ID發消息
     *
     * @param message 消息內容
     * @param id      連接會話ID
     * @return
     */
    @RequestMapping(value = "/sendOne", method = RequestMethod.GET)
    public String sendOneMessage(@RequestParam(required = true) String message,
                                 @RequestParam(required = true) String id) {
        try {
            WebSocketServer.SendMessage(message, id);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "ok";
    }
}

 

 

5、index.html接收后端發送的消息及展示

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>websocket測試</title>
    <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
    <style type="text/css">
        h3,h4{
            text-align:center;
        }
    </style>
</head>
<body>

<h3>WebSocket測試,客戶端接收到的消息如下:</h3>

<textarea id = "messageId" readonly="readonly" cols="150" rows="30" >

</textarea>


<script type="text/javascript">
    var socket;
    if (typeof (WebSocket) == "undefined") {
        console.log("遺憾:您的瀏覽器不支持WebSocket");
    } else {
        console.log("恭喜:您的瀏覽器支持WebSocket");
        //實現化WebSocket對象
        //指定要連接的服務器地址與端口建立連接
        //注意ws、wss使用不同的端口。我使用自簽名的證書測試,
        //無法使用wss,瀏覽器打開WebSocket時報錯
        //ws對應http、wss對應https。
        socket = new WebSocket("ws://localhost:8080/server");
        //連接打開事件
        socket.onopen = function() {
            console.log("Socket 已打開");
            socket.send("消息發送測試(From Client)");
        };
        //收到消息事件
        socket.onmessage = function(msg) {
            $("#messageId").append(msg.data+ "\n");
            console.log(msg.data  );
        };
        //連接關閉事件
        socket.onclose = function() {
            console.log("Socket已關閉");
        };
        //發生了錯誤事件
        socket.onerror = function() {
            alert("Socket發生了錯誤");
        }
        //窗口關閉時,關閉連接
        window.unload=function() {
            socket.close();
        };
    }
</script>

</body>
</html>

 

 

 

6、啟動項目,訪問頁面看效果

  訪問 localhost:8080,網頁展示如下

多看幾個頁面,頁面展示內容都一樣,同時后端控制台打印消息如下

接下來,使用postman,依次調用一對一消息推送和群發消息

一對一:http://localhost:8080/webSocket/sendOne?message="這是一條單個消息"&id=1

頁面展示如下:

群發消息:http://localhost:8080/webSocket/sendAll?message="這是一條群發消息"

頁面展示如下:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM