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

项目地址:https://github.com/fancaixia/wx_webSocket

chat.png



案例思路:

[1] 用户进入聊天室

  • 前台向后台发送连接请求并携带用户id
  • 后台 let clients:[]
  • clients.push(ws:'socket对象',id:'用户id')
  • 向前台广播当前聊天室人数

[2] 用户退出聊天室

  • 后台遍历clients 查找与当前用户id 对应数据在cliens中删除
  • 向前台广播当前聊天室人数

[3] 聊天室用户发普通消息时

  • 后台收到消息并且向每个ws对象广播消息

[4] 聊天室用户发图片时

  • 先将图片上传至服务器 (server / upload 目录)
  • 然后广播 图片地址
  • 前台接收图片路径并渲染页面

前台接收消息类型 type: message(普通消息) / image(图片消息) / loginin(进入聊天室) / loginout(退出聊天室)

~~~存在问题 微信小程序中onSocketOpen 在真机上不触发回调~~~
~~~后端数据存储没做研究 ~~~

代码结构:
  • 服务端代码

server

node_modules        项目依赖文件
router        处理上传文件模块
upload        图片上传目录
utils        工具函数
app.js        node主文件

  • 小程序端代码

static

utils        配置http及ws 地址
pages

home        首页,处理websocket 连接
chat        聊天室页面

项目启动:

cd / server
cnpm install
npm run dev

小程序端模拟多人聊天

copy static
static / app.js 修改 UserId 创建多个用户进入聊天室

小程序代码片段:

pages / home / index.js

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

Page({

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

  },

  // 创建 websocket  连接
  webSocket(user_id) {

    //loading 动画
    wx.showNavigationBarLoading()

    // 创建Socket 对象
    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连接创建', res)
      },
      fail: function (err) {
        wx.showToast({
          title: '网络异常!',
        })
        console.log(err)
      },
    })

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

        console.log('WebSocket 连接打开', 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(){

    // 后台发请求 建立连接 
    // 携带user_id  (此user_id 应为登陆后的user_id)
    this.webSocket(app.globalData.UserId);

  }

})

pages / chat / index.js

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

  data: {
     socketMsg : '',  // 发送消息
     msg_data:[],      //会话内容 
  },

  // 进入页面获取 app.globalData.SocketTask
  onLoad(){

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

  },
  // 退出页面 断开socket 连接
 onUnload(){

   this.close_websocket();
   

 },
  onReady () {

    let { SocketTask } = app.globalData;
   
    SocketTask.onClose(onClose => {
      // console.log('监听 WebSocket 连接关闭事件。', onClose)

      this.close_websocket();

    })
    SocketTask.onError(onError => {
      // console.log('监听 WebSocket 错误。错误信息', onError)

      this.close_websocket();

    })
    SocketTask.onMessage(msg => {
      console.log('服务端数据返回:',msg)

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

      // type 类型,message:普通消息,image:图片 , loginout:退出,  loginin:进入
      if (type == "message"){

        let { nickname, message, fromId } = JSON.parse(msg.data);
        // 根据app.globalData.UserId(当前用户id)判断发消息人
        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"){

        // 用户退出 / 进入 聊天室
        app.globalData.Chatusers = JSON.parse(msg.data).len;
        wx.setNavigationBarTitle({
          title: '聊天室人数 / ' + app.globalData.Chatusers
        })

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

        // 图片上传服务器成功后  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,
        })

      }

    })
  },

  // 断开连接
  close_websocket(){
    
    let { SocketTask } = app.globalData;

    if (app.globalData.SocketTask){
      SocketTask.close({
        code: 1000,
        success: (res) => {
          console.log('关闭成功', res)

          app.globalData.SocketTask = null;

        },
        fail: function (err) {
          console.log(err, "网络异常")
        },
        complete: () => {
          console.log('完成')
        }

      })
    }
    

  },

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

    // 检测空字符
    socketMsg = socketMsg.trim();
    if (socketMsg.length == 0){
      return;
    }

    SocketTask.send({
      data: socketMsg,
      success: (res) => {
        // console.log('发送成功', res)
        this.setData({
          socketMsg:''
        })
      },
      fail: function (err) {
        console.log(err,"网络异常")
      },

    })


  },
  // 图片上传
  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,"图片上传到服务器")
          }
        })

      }
    })
  }


});

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/'))) // 配置默认访问地址

let wss = new socketServer({port: 8080});    //创建websocketServer实例监听8080端口
let clientObj = require('./utils/setClients');  //创建客户端列表,用于保存客户端及相关连接信息

//监听连接
wss.on('connection', function(ws,req) {

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

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

    // 向前台发送聊天室用户信息
    utils.openSocket(ws, parseObj.query.id)

    /*监听并广播消息*/
    ws.on('message', function(message) {

        utils.broadcastSend(ws, "message", message, parseObj.query.id);
       
    });
    /*监听断开连接*/
    ws.on('close', function() {
        utils.closeSocket(ws, parseObj.query.id);
    })
})


app.use(router.routes());

app.listen(8090)


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,427评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,551评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,747评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,939评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,955评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,737评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,448评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,352评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,834评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,992评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,133评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,815评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,477评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,022评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,147评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,398评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,077评论 2 355