IM
Lightweight, extensible, JavaScript Instant Messaging.
功能
- 异常断开时,内部实现自动重连(下面几种方式即可无限重连)的情况:
- 服务端断网
- 服务端重启
- 客户端断网
- 不会自动重连的情况:
- 客户端主动断线(im.disconnect())
- 服务端主动断线(需要服务端发送一个断开连接的标识(前后端约定),在 MESSAG 监听到后,调用im.disconnect())
- 心跳机制。
- 监听客户端的连接状态
- 设置ping间隔
- 设置userId
- 区分会话类型(在发送消息时确定)
- server:0 server <-> user
- 单聊:1 user1 <-> user2
- 消息发送(默认时server会话类型)
- 文字消息
- 图片消息
- 文件消息
- ping消息(内置)
- 信令消息(在与服务交互时使用)
- 监听
- 消息监听
- 连接状态监听
- 信令消息的库扩展性(客户端和服务端可根据约定signalName实现业务层逻辑)
- 透传服务端发送的消息(content的消息透传)
注意事项
- 客户端主动发送消息,服务端收到消息并返回,从而形成一个闭环,才能证明客户端发送消息成功了。通过messageId才能形成闭环,也就是在客户端主动发消息情况下,服务端需返回客户端携带的messageId。客户端在send回调中拿到消息。
- 服务端主动给客户端发送消息不用携带messageId,客户端在消息监听中获取到消息。
- 需要在连接成功(监听 STATUS 为表示连接成功)后,才能发送消息、监听消息。
Usage
NPM、AMD and CommonJS module
参见集成客户端 demo(需注意:当前没有连接服务端)({your app path}/node_modules/awesome-im/src/example/client.html).
// 静态文件引入 awesome-im
// <script src="${path}awesome-im/dist/index.umd.js"></script>
// npm 安装、引入
// npm i awesome-im@latest
// import im from "awesome-im"
// websocket 地址
const url = "ws://localhost:8088";
// 初始化
im.init({
pingGap: 6000,
userId: "allenye" // 自定义用户ID
});
// 连接
im.connect(url).then(res => {
console.log("connect ->", res)
})
// 监听连接状态
im.addEventListener("STATUS", evt => {
console.log("连接状态->", evt)
})
// 监听消息
im.addEventListener("MESSAGE", evt => {
console.log("监听消息->", evt)
})
// 发送信令消息
im.send(new im.Message.SignalMessage({
signalName: "start", // 信令名称自定义,与服务端约定。
// signalName: "end",
to: "server"
})).then(res => {
console.log("发送SignalMessage成功", res)
})
// 发送文字消息
im.send(new im.Message.TextMessage({
// message: "message 可以扩展"
message: {
test: 123,
array: [123, 456],
object: {
name: "allen"
}
},
from: "allen",
to: "server"
})).then(res => {
console.log("发送TextMessage成功 ->", res)
})
// 修改会话类型(需要设置 conversationType),需要注意需要服务处理 user1、user2 之间的通信。
// 默认时server会话(im.Message.ConversationType.SERVER)
im.send(new im.Message.TextMessage({
conversationType: im.Message.ConversationType.PRIVATE, // 单聊会话
message: "发送一条单聊会话消息",
from: "user1",
to: "user2"
})).then(res => {
console.log("发送TextMessage成功 ->", res)
})
服务端 demo
参见集成服务端nodejs demo ({your app path}/node_modules/awesome-im/src/example/server.js).
// nodejs
const WebSocket = require('ws');
const { encode, decode } = require("@msgpack/msgpack");
const server = new WebSocket.Server({ port: 8088 });
server.on('connection', (socket) => {
console.log('Client connected');
// setInterval(() => {
// server.clients.forEach((client) => {
// client.send(encode({
// code: ErrorCode.SUCCESS,
// data: {
// messageType: MessageType.SIGNAL,
// sentTime: new Date().getTime(),
// messageUId: 'CBE5-1922-F8C9-730B',
// conversationType: ConversationType.SERVER,
// to: 'allen',
// signalName: 'end',
// from: 'server',
// content: {
// message: "一条服务端发送的消息"
// },
// },
// errMsg: "success!"
// }));
// })
// }, 2000);
// 当接收到消息时,向所有连接的客户端广播消息
socket.on('message', (message) => {
// console.log(`Received message: ${message}`);
const _data = decode(message)
server.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
const data = {
..._data,
time: new Date().getTime()
}
console.log(data)
client.send(encode({
code: ErrorCode.SUCCESS,
data: data
}));
}
});
});
// 当连接关闭时,向所有连接的客户端广播消息
socket.on('close', () => {
console.log('Client disconnected');
server.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(encode({ errMsg: "close", code: 4 }));
}
});
});
});
服务返回数据格式
服务端主动发送消息,不用携带 messageId。
{
"code": 0,
"data": {
"messageType": "SignalMsg",
"sentTime": 1681900543414,
"messageUId": "CBE5-1922-F8C9-730B",
"conversationType": 0,
"to": "allen",
"signalName": "login", // 可扩展
"from": "server",
"messageDirection": 2
},
"errMsg": "success!"
}
服务端收到客户端发送的消息后(服务端发送闭环消息),需要携带messageId
{
"code": 0,
"data": {
"messageType": "TextMsg",
"sentTime": 1681900543414,
"messageId": "**需要携带客户端发送消息的messageId,从而达到闭环。**",
"messageUId": "CBE5-1922-F8C9-730B",
"conversationType": 0,
"to": "allen",
"signalName": "end", // 可扩展
"from": "server",
"content": {
// 可以扩展
"message": "客户端发送消息后,服务端需要返回一条消息(闭环)。证明客户端发送的消息成功了。"
},
"messageDirection": 2
},
"errMsg": "success!"
}
消息、会话相关枚举数据
// 消息类型
export const MessageType = {
TEXT: "TxtMsg",
IMAGE: "ImgMsg",
FILE: "FileMsg",
PING: "PingMsg",
SIGNAL: "SignalMsg"
}
// 会话类型
export const ConversationType = {
/**
* 客户端与服务端的会话
*/
SERVER: 0,
/**
* 单聊
*/
PRIVATE: 1,
/**
* 讨论组
*/
DISCUSSION: 2,
/**
* 群组聊天
*/
GROUP: 3,
/**
* 聊天室会话
*/
CHATROOM: 4,
/**
* 系统消息
*/
SYSTEM: 5,
}
// 消息方向
export const MessageDirection = {
/**
* 发送消息。
*/
SEND: 1,
/**
* 接收消息。
*/
RECEIVE: 2
}
// 事件监听
const event = {
STATUS: "STATUS",
MESSAGE: "MESSAGE",
TEST: "TEST",
}