演示 Link to heading

img

功能 Link to heading

  • 群聊,每个进入的用户都可以看到自己和他人的聊天记录

  • 实时显示当前在线人数

  • 自己发送的消息和他人有区分

服务端 Link to heading

const express = require('express');
const app = express();
const WebSocket = require('ws');
const serve = app.listen(3001,function () {
    console.log('server run 3001');
});
const ws = new WebSocket.Server({
    server: serve
});

let clientsUser = {};

// 获取连接个数
function allConnections(ws) {
    let i = 0;
    ws.clients.forEach(function each() {
        i+=1
    });
    console.log(`[SERVER] 共有${i}个连接`);
    return i;
}

// 广播
function broadcast(data) {
    let num = allConnections(ws);
    ws.clients.forEach(function each(client) {
        client.send(data);
    });
    console.log(`[SERVER] 给${num}个用户发送了广播消息`);
}

// 判断id
function checkId(id) {
    let resArray = id.split("_");
    if (resArray[0] === 'user'){
        return 'user';
    }
    if (resArray[0] === 'manage'){
        return 'manage';
    }
}

// 判断id是否存在
function checkIdExist(clientsUser,id) {
    for (key in clientsUser){
        if (key === id){
            return true;
        }
    }
    return false;
}

// 发送目前总连接数
function sendCountNum(ws) {
    let broadcastMsg = JSON.stringify({
        "rstCode": "0",
        "rstMsg": {
            "count": allConnections(ws)
        }
    });
    broadcast(broadcastMsg);
}

// 监听连接
ws.on('connection',function (result) {
    console.log('客户端正在连接中...');

    // 监听发送消息
    result.on('message',function (message) {
        let res = JSON.parse(message),
            checkRes = checkId(res.websocketId);
        // 检测是否是用户
        if (checkRes === 'user'){
            // 检测如果是登录状态则保存用户信息
            if (res.status === 'login'){
                // 检测服务端是否存储用户信息,存储则删旧添新,没有则直接添加
                if (checkIdExist(clientsUser,res.websocketId)){
                    delete clientsUser[res.websocketId];
                    clientsUser[res.websocketId] = result;
                    // 广播目前总连接数
                    sendCountNum(ws);
                }else {
                    clientsUser[res.websocketId] = result;
                    // 广播目前总连接数
                    sendCountNum(ws);
                }
            }
            // 发送信息操作
            if (res.status === 'send'){
                let msg = JSON.stringify({
                    "rstCode": "0",
                    "rstMsg": {
                        "count": allConnections(ws),
                        "msg": res.msg,
                        "websocketId": res.websocketId,
                        "nickname": res.nickname
                    }
                })
                broadcast(msg);
            }
        }
    })

    // 监听下线
    result.on('close', function (close) {
        for (key in clientsUser){
            if (clientsUser[key] === result){
                delete clientsUser[key];
                console.log(`[SERVER] 客户端${key}连接已关闭`);
            }
        }
        // 广播目前的连接数
        let msg = JSON.stringify({
            "rstCode": "0",
            "rstMsg": {
                "count": allConnections(ws)
            }
        })
        broadcast(msg);
    })
})

客户端 Link to heading

wxml

<!--index.wxml-->
<view class="container">
  <scroll-view scroll-y style='height: {{_height}}' class='chat-room-content'>
    <view class='chat-counts'>当前有{{ countNum }}人正在讨论</view>
    <view wx:for="{{ msgArr }}" wx:key="*this" class='chat-info'>
      <text class="chat-info-name {{ item.websocketId === websocketId? 'chat-info-user':'' }}">{{ item.nickname }}:</text>
      <text class="chat-info-content {{ item.websocketId === websocketId? 'chat-info-user':'' }}">{{ item.msg }}</text>
    </view>
  </scroll-view>
  <view id='chatRoomBottom' class='chat-room-bottom flex center-center'>
    <view class='chat-room-ipt flex-10 flex'>
      <view class='chat-room-ipt-ico flex-1 flex center-center'>
        <image src='../../assets/imgs/comment.png'></image>
      </view>
      <view class='flex-11'>
        <input type='text' value="{{ msg }}" confirm-type="send" bindconfirm="sendMsg" placeholder-class='ipt-ph' placeholder='一起聊天吧' bindinput='onChatInfoChange'></input>
      </view>
    </view>
    <view class='chat-room-btn flex-2'>
      <button bindtap='sendMsg'>发 送</button>
    </view>
  </view>
</view>

js

Page({

  /**
   * 页面的初始数据
   */
  data: {
    _height: '',
    msg: '',
    countNum: 0,
    msgArr: [],
    websocketId: ''
  },
  sendMsg(){
    console.log(wx.getStorageSync('userInfo').nickName);
    let msg = {
      "websocketId": this.data.websocketId,
      "status": "send",
      "nickname": wx.getStorageSync('userInfo').nickName,
      "msg": this.data.msg
    }
    wx.sendSocketMessage({
      data: JSON.stringify(msg)
    })
    this.setData({
      msg: ''
    })
  },
  onChatInfoChange(e){
    console.log(e.detail.value);
    this.setData({
      msg: e.detail.value
    })
  },
  scrollHeight(){
    let _this = this;
    wx.getSystemInfo({
      success: function(res) {
        // 获取可用高度px
        let windowHeight = res.windowHeight;
        _this.setData({
          _height: (windowHeight - 61) + 'px'
        })
      }
    })
  },
  connectWebsocket() {
    let _this = this;
    let loginUserInfo = {
      'websocketId': this.data.websocketId,
      'status': 'login'
    }
    wx.connectSocket({
      url: 'ws://192.168.1.108:3001'
    })
    wx.onSocketOpen(function (re) {
      console.log('WebSocket连接已打开!');
      // 连接成功发送socket信息
      wx.sendSocketMessage({
        data: JSON.stringify(loginUserInfo)
      })
      // 监听服务器返回
      wx.onSocketMessage(function (result) {
        console.log('收到服务器内容:' + result.data);
        let res = JSON.parse(result.data), 
            msgArr = _this.data.msgArr;
        console.log(res)
        if (res.rstMsg.msg){
          msgArr.push(res.rstMsg);
          _this.setData({
            msgArr: msgArr
          })
        }
        else{
          _this.setData({
            countNum: res.rstMsg.count
          })
        }
      })
    })
  },
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    this.scrollHeight();
    this.setData({
      websocketId: 'user_'+ wx.getStorageSync('openId')
    })
    // 建立websocket连接
    this.connectWebsocket();
    // 监听websocket断开
    wx.onSocketError(function (res) {
      console.log('WebSocket由于错误已关闭!');
    })
    wx.onSocketClose(function (res) {
      console.log('WebSocket 已关闭!');
    })
  },

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload: function () {
    wx.closeSocket();
  }
})

wxss

.chat-room-bottom{
  position: fixed;
  bottom: 0rpx;
  left: 0rpx;
  right: 0rpx;
  z-index: 100;
  background: #fff;
  padding: 15rpx 20rpx;
}
.chat-room-ipt-ico{
  padding-right: 10rpx;
}
.chat-room-ipt-ico image{
  width: 13px;
  height: 13px;
}
.chat-room-btn{
  padding-left: 20rpx;
}
.chat-room-btn button{
  border-radius: 2px;
  background: #396bd0;
  font-size: 12px;
  color: #fff;
  padding: 0rpx 15rpx;
  line-height: 45px;
}
.chat-room-btn button::after{
  border-radius: 2px!important;
}
.chat-room-ipt{
  padding: 0 10rpx;
  border: 1rpx solid #dfdfdf;
  border-radius: 2px!important;
}
.ipt-ph{
  font-size: 12px;
  color: #aaa;
}
.chat-room-ipt input{
  font-size: 12px;
  color: #555;
  height: 45px;
}
.chat-room-content{
  box-sizing: border-box;
  width: 100%;
  padding: 0 20rpx;
}
.chat-info{
  margin-top: 15rpx;
  font-size: 12px;
}
.chat-info:last-child{
  margin-bottom: 15rpx;
}
.chat-info-name{
  color: #729aea;
}
.chat-info-content{
  color: #555;
}
.chat-info-user{
  color: #f1c24e!important;
}
.chat-counts{
  margin: 20rpx 0;
  font-size: 12px;
  color: #555;
  font-weight: 500;
  text-align: center;
}