node + WebSocket 微信小程序多人聊天室

項(xiàng)目地址:https://github.com/fancaixia/wx_webSocket

chat.png



案例思路:

[1] 用戶進(jìn)入聊天室

  • 前臺(tái)向后臺(tái)發(fā)送連接請(qǐng)求并攜帶用戶id
  • 后臺(tái) let clients:[]
  • clients.push(ws:'socket對(duì)象',id:'用戶id')
  • 向前臺(tái)廣播當(dāng)前聊天室人數(shù)

[2] 用戶退出聊天室

  • 后臺(tái)遍歷clients 查找與當(dāng)前用戶id 對(duì)應(yīng)數(shù)據(jù)在cliens中刪除
  • 向前臺(tái)廣播當(dāng)前聊天室人數(shù)

[3] 聊天室用戶發(fā)普通消息時(shí)

  • 后臺(tái)收到消息并且向每個(gè)ws對(duì)象廣播消息

[4] 聊天室用戶發(fā)圖片時(shí)

  • 先將圖片上傳至服務(wù)器 (server / upload 目錄)
  • 然后廣播 圖片地址
  • 前臺(tái)接收?qǐng)D片路徑并渲染頁(yè)面

前臺(tái)接收消息類型 type: message(普通消息) / image(圖片消息) / loginin(進(jìn)入聊天室) / loginout(退出聊天室)

~~~存在問(wèn)題 微信小程序中onSocketOpen 在真機(jī)上不觸發(fā)回調(diào)~~~
~~~后端數(shù)據(jù)存儲(chǔ)沒(méi)做研究 ~~~

代碼結(jié)構(gòu):
  • 服務(wù)端代碼

server

node_modules ?????? 項(xiàng)目依賴文件
router ?????? 處理上傳文件模塊
upload ?????? 圖片上傳目錄
utils ?????? 工具函數(shù)
app.js ?????? node主文件

  • 小程序端代碼

static

utils ?????? 配置http及ws 地址
pages

home ?????? 首頁(yè),處理websocket 連接
chat ?????? 聊天室頁(yè)面

項(xiàng)目啟動(dòng):

cd / server
cnpm install
npm run dev

小程序端模擬多人聊天

copy static
static / app.js 修改 UserId 創(chuàng)建多個(gè)用戶進(jìn)入聊天室

小程序代碼片段:

pages / home / index.js

// pages/home/index.js
let config = require('../../utils/config.js')
let app = getApp();

Page({

  data: { },
  onLoad: function (options) {

  },

  // 創(chuàng)建 websocket  連接
  webSocket(user_id) {

    //loading 動(dòng)畫
    wx.showNavigationBarLoading()

    // 創(chuàng)建Socket 對(duì)象
    app.globalData.SocketTask = wx.connectSocket({
      url: `${config.wsAddress}?id=${user_id}`,
      data: '',
      header: {
        'content-type': 'application/json'
      },
      method: 'post',
      success: function (res) {
        console.log('WebSocket連接創(chuàng)建', res)
      },
      fail: function (err) {
        wx.showToast({
          title: '網(wǎng)絡(luò)異常!',
        })
        console.log(err)
      },
    })

    app.globalData.SocketTask.onOpen(res => {

        console.log('WebSocket 連接打開(kāi)', res)
        wx.hideNavigationBarLoading()
        wx.navigateTo({
          url: '/pages/chat/index'
        })

    })

    app.globalData.SocketTask.onError(res => {

      console.log('WebSocket 連接失敗', res)

    })

    app.globalData.SocketTask.onMessage(msg => {

      app.globalData.Chatusers = JSON.parse(msg.data).len;

    })

  },
  gotochat(){

    // 后臺(tái)發(fā)請(qǐng)求 建立連接 
    // 攜帶user_id  (此user_id 應(yīng)為登陸后的user_id)
    this.webSocket(app.globalData.UserId);

  }

})

pages / chat / index.js

let config = require('../../utils/config.js')
let app = getApp();
Page({

  data: {
     socketMsg : '',  // 發(fā)送消息
     msg_data:[],      //會(huì)話內(nèi)容 
  },

  // 進(jìn)入頁(yè)面獲取 app.globalData.SocketTask
  onLoad(){

    wx.setNavigationBarTitle({
      title: '聊天室人數(shù) / ' + app.globalData.Chatusers
    })

  },
  // 退出頁(yè)面 斷開(kāi)socket 連接
 onUnload(){

   this.close_websocket();
   

 },
  onReady () {

    let { SocketTask } = app.globalData;
   
    SocketTask.onClose(onClose => {
      // console.log('監(jiān)聽(tīng) WebSocket 連接關(guān)閉事件。', onClose)

      this.close_websocket();

    })
    SocketTask.onError(onError => {
      // console.log('監(jiān)聽(tīng) WebSocket 錯(cuò)誤。錯(cuò)誤信息', onError)

      this.close_websocket();

    })
    SocketTask.onMessage(msg => {
      console.log('服務(wù)端數(shù)據(jù)返回:',msg)

      let { msg_data } = this.data;
      let { type } = JSON.parse(msg.data);

      // type 類型,message:普通消息,image:圖片 , loginout:退出,  loginin:進(jìn)入
      if (type == "message"){

        let { nickname, message, fromId } = JSON.parse(msg.data);
        // 根據(jù)app.globalData.UserId(當(dāng)前用戶id)判斷發(fā)消息人
        let fromuser = 'other'
        if (fromId == app.globalData.UserId){
          fromuser = 'me'
        }

        msg_data.push({ type, nickname, message, fromId, fromuser })
        this.setData({
          msg_data,
        })

      } else if (type == "loginout" || type == "loginin"){

        // 用戶退出 / 進(jìn)入 聊天室
        app.globalData.Chatusers = JSON.parse(msg.data).len;
        wx.setNavigationBarTitle({
          title: '聊天室人數(shù) / ' + app.globalData.Chatusers
        })

      } else if (type == "image" ){

        // 圖片上傳服務(wù)器成功后  ws廣播
        let { msg_data } = this.data;
        let { src, type, fromId } = JSON.parse(msg.data);

        let fromuser = 'other'
        if (fromId == app.globalData.UserId) {
          fromuser = 'me'
        }
        msg_data.push({ type, src: `${config.httpAddress}/${src}`, fromId, fromuser, })

        this.setData({
          msg_data,
        })

      }

    })
  },

  // 斷開(kāi)連接
  close_websocket(){
    
    let { SocketTask } = app.globalData;

    if (app.globalData.SocketTask){
      SocketTask.close({
        code: 1000,
        success: (res) => {
          console.log('關(guān)閉成功', res)

          app.globalData.SocketTask = null;

        },
        fail: function (err) {
          console.log(err, "網(wǎng)絡(luò)異常")
        },
        complete: () => {
          console.log('完成')
        }

      })
    }
    

  },

  // 更改 inpiut  value
  setstr(e){
      this.setData({
        socketMsg : e.detail.value
      })
  },
// 發(fā)送消息  
  sendMsg(){
    let { socketMsg } = this.data; 
    let { SocketTask } = app.globalData;

    // 檢測(cè)空字符
    socketMsg = socketMsg.trim();
    if (socketMsg.length == 0){
      return;
    }

    SocketTask.send({
      data: socketMsg,
      success: (res) => {
        // console.log('發(fā)送成功', res)
        this.setData({
          socketMsg:''
        })
      },
      fail: function (err) {
        console.log(err,"網(wǎng)絡(luò)異常")
      },

    })


  },
  // 圖片上傳
  add_photo(){
    let $this = this;

    wx.chooseImage({
      success(res) {

        const tempFilePaths = res.tempFilePaths

        const uploadTask = wx.uploadFile({
          url: `${config.httpAddress }/upfile/add`, // 圖片上傳接口地址
          filePath: tempFilePaths[0],
          name: 'file',
          formData: {
            'fromId': app.globalData.UserId
          },
          success(res) {
            // console.log(res,"圖片上傳到服務(wù)器")
          }
        })

      }
    })
  }


});

node / app.js
let koa = require('koa')
const staticFile = require('koa-static')
let router = require('./router/upfile')
let path = require('path')
let url = require('url')

let ws = require('ws')
let socketServer = ws.Server
let utils = require('./utils/utils')


let app = new koa();
app.use(staticFile(path.join(__dirname + '/upload/'))) // 配置默認(rèn)訪問(wèn)地址

let wss = new socketServer({port: 8080});    //創(chuàng)建websocketServer實(shí)例監(jiān)聽(tīng)8080端口
let clientObj = require('./utils/setClients');  //創(chuàng)建客戶端列表,用于保存客戶端及相關(guān)連接信息

//監(jiān)聽(tīng)連接
wss.on('connection', function(ws,req) {

    var parseObj = url.parse(req.url, true);

    clientObj.add({
        "id": parseObj.query.id,
        "ws": ws,
    })

    // 向前臺(tái)發(fā)送聊天室用戶信息
    utils.openSocket(ws, parseObj.query.id)

    /*監(jiān)聽(tīng)并廣播消息*/
    ws.on('message', function(message) {

        utils.broadcastSend(ws, "message", message, parseObj.query.id);
       
    });
    /*監(jiān)聽(tīng)斷開(kāi)連接*/
    ws.on('close', function() {
        utils.closeSocket(ws, parseObj.query.id);
    })
})


app.use(router.routes());

app.listen(8090)


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容