SpringBoot框架:集成websocket實現即時通訊

一、導入依賴

  分別導入websocket依賴包和處理json格式數據的fastjson依賴包。

<dependency>
      <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
      <groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.60</version>
</dependency>

二、註解處理

  以下註解可以將配置爲@ServerEndpoint標註的類在調用時自動導出爲實體,類編寫完成後使用@Configuration將config加入配置。

package com.example.demo2.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();
    }
}

三、websocket類編寫

package com.example.demo2.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.util.JSONPObject;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;

@Component
@ServerEndpoint("/websocket/{userId}")
public class MessageController {

    //與某個客戶端的連接會話,需要用來發送數據
    private Session session;

    //concurrent線程安全set,存放每個客戶端對應的websocket對象
    private static CopyOnWriteArraySet<MessageController> messageControllers = new CopyOnWriteArraySet<>();

    //存儲session池
    private static Map<String,Session> sessionPool = new HashMap<String,Session>();

    /**
     * 鏈接成功調用
     */
    @OnOpen
    public void onOpen(Session session, @PathParam(value = "userId")String userId){
        try{
            //session獲取
            this.session = session;
            //靜態存儲的websocket長鏈接集合中,添加本次調用成功所創建的這個實體
            messageControllers.add(this);
            //session緩存池添加用戶的用戶ID和對應的session
            sessionPool.put(userId,session);
            //將用戶加入鏈接和當前連接數的消息廣播給當前所有已連接的用戶
            sendAllMessage("【" + userId +"】加入鏈接,當前連接數:" + messageControllers.size());
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 鏈接關閉調用
     */
    @OnClose
    public void onClose(){
        try{
            //已連接的對象列表中移除當前調用此關閉方法的對象
            messageControllers.remove(this);
            //將用戶斷開鏈接和鏈接總數信息推送給現在還鏈接着的用戶
            System.out.println("用戶斷開鏈接,當前鏈接總數爲:" + messageControllers.size());
        }catch (Exception e){

        }
    }

    /**
     * 收到客戶端消息
     */
    @OnMessage
    public void onMessage(String message){
        //收到客戶端消息後可以根據實際需求做一些處理操作,也可以直接返回,到頁面進行處理
        //廣播給羣中現在處理已連接狀態的所有用戶
        sendAllMessage(message);
    }

    /**
     * 發送錯誤處理
     */
    @OnError
    public void onError(Session session,Throwable error){
        //輸出錯誤信息
        System.out.println("【發送錯誤】:" + error.getMessage());
        error.printStackTrace();
    }

    /**
     * 廣播(全體)消息
     */
    public void sendAllMessage(String message){
        //遍歷當前所有已連接的用戶,循環發送
        for(MessageController messageController : messageControllers){
            try{
                //session處於打開狀態,則發送消息
                if(messageController.session.isOpen()){
                    //最好是使用這個getAsyncRemote,另一個(getBasicRemote)在多個線程同時進行時容易報錯,這兩個是異步和同步的區別
                    messageController.session.getAsyncRemote().sendText(message);
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

    /**
     * 單點消息
     */
    public void sendOneMessage(String userId,String message){
        //根據userId獲取到對應的session
        Session session = sessionPool.get(userId);
        //這個session不爲空並且還在開放狀態
        if(session != null && session.isOpen()){
            try{
                //將消息單獨推送到這個userId對應的session裏
                session.getAsyncRemote().sendText(message);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

    /**
     * 多點消息(多選發給一部分人)
     */
    public void sendManyMessage(String[] userIds,String message){
        //遍歷參數中的用戶id數組,進行多個推送,介於單個和廣播(全體)之間的部分發送
        for(String userId:userIds){
            Session session = sessionPool.get(userId);
            if(session != null && session.isOpen()){
                try{
                    session.getAsyncRemote().sendText(message);
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }

}

  除了在這個類內部調用發送信息的方法之外,其他可以通過類中注入此類後,使用接口進行方法調用。(之前本來想用main方法測試一下發現這種方式不行,還是要在接口中調用)

四、websocket在vue頁面裏進行應用

<template>
  <div>
    <!--填寫個人信息  -->
    <input v-model="userId" />
    <input v-model="userName" />
    <!-- 綁定後根據個人信息創建websocket連接 -->
    <button @click="andUser">綁定</button>
    <br />
    <!-- 填寫要發送的消息 -->
    <input v-model="message" />
    <!-- 發送信息 -->
    <button @click="webSocketSend">發送</button>
    <br />
    <!-- 遍歷顯示所有信息列表 -->
    <div v-for="item in infoList">
      <span>{{ item }}</span>
      <br />
    </div>
  </div>
</template>

<script>
    export default {
      name: "Index",
      data(){
        return{
          //默認用戶信息
          userId:1,
          userName:"張三",
          //預置websocket,設爲undefined
          websock:undefined,
          //要發送的消息
          message:"",
          //消息列表
          infoList:[],
        }
      },
      mounted() {
        //如果多地進行測試,可以在這裏進行初始化
        //一個人開多個窗口的話,可以在填寫不同用戶信息後,點擊綁定時使用不同的userId進行初始化
      },
      destroyed() {
        //頁面銷燬時觸發,關閉此用戶的websocket連接
        this.webSocketOnClose();
      },
      methods:{
        //建立連接
        initWebSocket(){
          //websocket服務鏈接的地址
          let url = "ws://127.0.0.1:8080/websocket/" + this.userId;
          //創建連接
          this.websock = new WebSocket(url);
          //將自定義的方法替換websocket自帶的方法
          this.websock.onopen = this.webSocketOnOpen;
          // this.websock.send = this.webSocketSend;
          this.websock.onerror = this.webSocketOnError;
          this.websock.onmessage = this.webSocketOnMessage;
          this.websock.onclose = this.webSocketOnClose;
        },
        //連接上之後觸發
        webSocketOnOpen(){
          //將用戶連接成功的消息放到列表進行展示
          this.infoList.push("" + this.userName + "】連接成功");
        },
        //發送消息時觸發
        webSocketSend(){
          //將用戶信息和要發送的消息包裝爲json
          let data = {
            userId:this.userId,
            userName:this.userName,
            message:this.message
          }
          //將json格式化爲字符串之後發送到服務端
          this.websock.send(JSON.stringify(data));
          //自己發送的信息添加到列表進行展示
          this.infoList.push(this.userName + ":" + this.message);
        },
        //發送錯誤時觸發
        webSocketOnError(e){
          //將錯誤信息添加到列表展示在頁面
          this.infoList.push("【錯誤】:" + e.toString());
        },
        //接收消息時觸發
        webSocketOnMessage(e){
          //返回的信息存儲在此方法參數的data字段
          if(e.data != undefined && e.data != null && e.data != ''){
            //信息非空時,通過json格式化爲表單
            let json = JSON.parse(e.data);
            //如果返回消息的用戶id是本人,則爲自己發送的信息,不需要顯示
            if(json.userId != this.userId){
              //判斷是別人發送的信息,則拼接字段並添加到列表中用於展示
              this.infoList.push(json.userName + ":" + json.message);
            }
          }
        },
        //關閉連接時觸發
        webSocketOnClose(){
          //將用戶關閉鏈接的消息寫入列表
          this.infoList.push("" + this.userName + "】關閉連接");
        },
        //綁定用戶
        andUser(){
          //初始化websocket
          this.initWebSocket();
        }
      }
    }
</script>

<style scoped>

</style>

  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章