動態

詳情 返回 返回

uni.connectSocket與@microsoft/signalr連接 - 動態 詳情

之前做web頁面的時候,前後端用的@microsoft/signalr庫做長連接,現在微信小程序不支持signalr庫的直接使用,uniapp微信小程序的websocket長連接只能用uni.connectSocket,所以我們要用uni.connectSocket實現與signalr庫的長連接。
頁面結構

<view>
        <button @click="onLink">啓動連接</button>
        <button @click="onSend">發送消息</button>
        <button @click="stopLink">停止</button>
</view>

先封裝一個uni.connectSocket類,該類可以直接複製到你的項目中使用,例如新建一個socket.js文件

class WebSocketClient {
    constructor(url, reconnectTimer, token) {
        this.url = url; // wss url
        this.SocketTask = null; // uni.connectSocket實例,SocketTask
    this.timer = null; // 定時器-斷線重連
        this.reconnectTimer = reconnectTimer; // 重連時長
        this.token = token; // token
        this.reconnectCount = 0; // 重連次數
        this.maxReconnectCount = 5; // 最大重連數
    // 響應回調
        this.callbacks = {
            onMessage: null,
            onOpen: null,
            onClose: null,
            onError: null
        };
    // 建立連接
        this.connect();
    }

  // 連接websocket
    connect() {
        this.SocketTask = uni.connectSocket({
            url: this.url + '?access_token=' + this.token,
            method: 'GET',
            success: (e) => {
                console.log('連接成功', e);
            },
            fail: (e) => {
                console.log('連接失敗', e);
            }
        })

    // 連接打開
        this.SocketTask.onOpen((e) => {
      // 重要!!!
      // 連接打開的時候發送一條消息
      // 告訴服務器使用 JSON 的形式進行消息的序列化
            this.SocketTask.send({
                data: `{"protocol":"json", "version":1}${String.fromCharCode(0x1e)}`
            });
            this.callbacks.onOpen?.(e);
        });

    // 連接響應
        this.SocketTask.onMessage((e) => {
            this.callbacks.onMessage?.(e);
        });

    // 連接關閉
        this.SocketTask.onClose((e) => {
            this.callbacks.onClose?.(e);
        });

    // 連接錯誤
        this.SocketTask.onError((e) => {
            this.callbacks.onError?.(e);
            this.reconnect(); // 連接錯誤時開啓重連
        });
    }

    // 註冊傳入的回調函數,用於頁面響應
    on(event, callback) {
        if (this.callbacks.hasOwnProperty(event)) {
            this.callbacks[event] = callback;
        }
        return this;
    }

    // 斷線重連
    reconnect() {
        if (this.reconnectCount < this.maxReconnectCount) {
            this.reconnectCount++;
            this.reconnectTimer = setTimeout(() => {
                this.connect();
            }, 2000 * this.reconnectCount);
        }
    }

    // 發送消息
    send(data) {
        this.SocketTask.send({
            data,
            success: (e) => {
                console.log('發送成功', e);
            },
            fail: (e) => {
                console.log('發送失敗', e);
            }
        });
    }

    // 關閉連接
    close() {
        this.SocketTask.close({
            code: 1000,
            success: (e) => {
                console.log('關閉成功', e);
            },
            fail: (e) => {
                console.log('關閉失敗', e);
            }
        });
    }
}

export default WebSocketClient;

引入封裝的uni.connectSocket,頁面中使用

// 連接socket
let SocketTask = null;
const onLink = () => {
    indexs = 0;
    let url = `wss://xxx.xxxxxxxx.com/api/ChatHub`;
    let token = `Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxx`;
  // 連接websocket並註冊響應事件
    SocketTask = new WebSocketClient(url, 5000, token).on('onOpen', onOpen).on('onMessage', onMessage).on('onClose', onClose).on('onError', onError);
};

const onOpen = (e) => {
    console.log('連接打開', e);
};

const onMessage = (e) => {
    console.log('連接響應',e)
};

const onClose = (e) => {
    console.log('連接關閉', e);
};

const onError = (e) => {
    console.log('連接錯誤', e);
};

// 發送消息
const onSend = () => {
  // 你要發送的內容
    let data = [{ text: '今天天氣晴朗', isFinish: false }];
  // 與後端匹配的方法名稱
    let methodName = 'SendModelReplyText';
  // type: 1 是固定值
  // 發送消息,格式是固定的,否則後端signalr不識別
    SocketTask.send(`${JSON.stringify({ arguments: data, target: methodName, type: 1 })}${String.fromCharCode(0x1e)}`);
};

發送消息後,websocket會響應,我們再頁面上已經寫了響應函數,這裏要稍微改動下

const onMessage = (e) => {
    if (!e.data) return;
  // 解析規則是固定的
    let msg = e.data.replace(String.fromCharCode(0x1e), ''); //替換消息結束符
    let msgData = JSON.parse(msg);
  console.log('響應', msgData)
};

// 停止連接
const stopLink = () => {
  // 你要發送的內容
    let data = [{text: '',isFinish: true}];
  // 與後端匹配的方法名稱
    let methodName = 'SendModelReplyText';
  // type: 1 是固定值
  // 發送消息,格式是固定的,否則後端signalr不識別
    SocketTask.send(`${JSON.stringify({ arguments: data, target: methodName, type: 1 })}${String.fromCharCode(0x1e)}`);

  // 這裏不能直接SocketTask.close();
  // 因為連接是根據websocket來的,我們發送isFinish: true給後端後,應該後websokcet返回真實可斷開標記
  // 然後在onMessage響應函數裏判斷isFinish: true時再SocketTask.close();
};

uni.connectSocketsignalr的長連接主要在於發送前端的內容後端不識別,如果不做轉碼發送,後端很大慨率會返回“中斷握手請求” ,下面幾個點是關鍵:
1、建立連接後發送一條消息,告訴服務器使用 JSON 的形式進行消息的序列化
2、send消息時要轉碼發送,結構必須是signalr可識別的
3、onMessage接收響應消息時要解碼,否則前端用不了
4、關閉連接也要轉發發送,並且不能直接關閉,需要在響應中判斷是否可真實關閉再斷開連接。


參考文章:
uniapp 原生websocket 使用 signalr

user avatar maomaoxiaobo 頭像 zzd41 頭像
點贊 2 用戶, 點贊了這篇動態!
點贊

Add a new 評論

Some HTML is okay.