最近做了聊天,功能点大概如下:
- 群聊
- @全员
- 群主禁言
- 只看群主
- 聊天表情😊
技术选择
- 基于
Socket.io做技术基础 - 基于
node-redis做聊天消息缓存 - 基于
node-schedule定时存储Redis中的聊天消息
服务器端
const server = require('http').createServer();
const io = require('socket.io')(server, {
path: '/socket/chat',
serveClient: false,
// below are engine.IO options
pingInterval: 10000,
pingTimeout: 5000,
cookie: false
});
server.listen(3000);
客户端
- 如有需要代理
/socket/chat/与path设置一致 - 不设置
path,就是默认的socket.io
proxy: {
"/socket/chat/*": {
"target": "ws://localhost:8000/socket/chat",
"ws": true
},
}
- 连接socket
const ws = io('localhost:4000', {
path: '/socket/chat',
transports: ['websocket'],
reconnection: true,
});
ws.on('connect', () => {});
ws.on('error', error => {
console.log(error);
});
ws.on('disconnect', reason => {
console.log('disconnect', reason);
});
window.addEventListener('beforeunload', () => {
ws.emit('client:disconnect');
});
- Web端加入房间
// Web端
socket.emit('room:join', id);
// Server端
socket.on('room:join', roomId => {
socket.join(roomId, error => {
if (error) {
server.log([...errorTags, 'join'], { roomId, error });
}
socket.on('chat:room:user-send', listener);
});
});
- 进入房间后,监听Server端的消息
// Web端
socket.on('chat:room:server-send', this.handleSocket);
// Server端
socket.to(msg.roomId).emit('chat:room:server-send', newMsg);
- 聊天消息存储到Redis
await redis.ZADDAsync(key, msg.time, JSON.stringify(msg));
- 分页获取Redis消息
// Web端
// 获取历史消息
socket.emit(
'room.msg:list',
{ roomId: id, ps, pn, isBottom: true },
this.handleHistoryMessage
);
// Server端
socket.on(
'room.msg:list',
async ({ roomId, ps = 20, pn = 1 }, fn) => {
const key = getKey('CHAT:MSG', roomId);
const data = await redis.zrevrangebyscoreAsync(
key,
Date.now(),
0,
'LIMIT',
(pn - 1) * ps,
pn * ps
);
const list = data.map(it => JSON.parse(it));
fn({ list, pn });
}
);
- 定时存储到Mongo,未完待续...
- 表情未完待续...
- socket建立太多带来的单机使用问题...
相关方法
- 初始化历史消息或者每次接受到聊天消息时,自动滚动到底部,查看最新消息
// 滚动到底部
scrollTop = () => {
setTimeout(() => {
this.newsCon.scrollTop = 100000000;
}, 200);
};
- 上滑加载更多历史消息
_onScrollEvent = () => {
const { id } = this.props;
let { pn } = this.state;
if (this.newsCon.scrollTop === 0) {
pn += 1;
socket.emit(
'room.msg:list',
{ roomId: id, ps, pn },
this.handleHistoryMessage
);
}
};
// React中div
<div
className="news-con"
ref={e => {
this.newsCon = e;
}}
onScrollCapture={debounce(this._onScrollEvent, 100)}
>
</div>
相关技术学习文章
End
- 第一次做聊天相关技术,有任何问题欢迎交流