小程序实现聊天室功能(WebSocket技术)

演示

img

功能

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

  • 实时显示当前在线人数

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

服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
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);
})
})

客户端

wxml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!--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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
.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;
}