在仿比心一对一直播软件源码中,消息功能起着非常重要的作用,非实时性的社交都可以通过消息功能来实现,在即时消息模块有两个基本功能就是发送消息和接收消息。我们定义如下通信指令来实现这两个功能:
Push 推送消息
Pull 拉取消息
Notify 消息通知
Push 推送消息指令将仿比心一对一直播软件源码客户端消息发给指定的对端,也就是说服务器需要在收到客户端 Push 指令时将消息转发给目标客户端。
Pull 拉取消息指令用于仿比心一对一直播软件源码客户端在必要时(例如,移动平台的 App 从后台回到前台时)从服务器端获取未被即时推送的消息。
Notify 消息通知是这三个指令里唯一个服务器发给仿比心一对一直播软件源码客户端的指令,用来通知客户端“你有新消息送达”。客户端在收到该指令后即可将消息进行存储和呈现。
数据包结构
我们定义一个 Packet 结构来封装我们的数据包,Packet 包含三个主要属性:序列号、名称、负载。
序列号 - 标识每个 Packet 的序号,该序列号唯一标识了一个 Packet 。
名称 - 标记该 Packet 的名称,我们使用名称来表示不同业务操作。
负载 - Packet 携带的数据,这里我们约定负载遵循 JSON 格式。
服务器代码
如前所述,服务器需要接收来自仿比心一对一直播软件源码客户端的 Push 和 Pull 指令,我们需要在 Cellet 里实现对这两个数据包的接收处理:
public class MessagingCellet extends Cellet {
private ExecutorService executor;
public MessagingCellet() {
super("Messaging");
}
@Override
public boolean install() {
...
}
@Override
public void uninstall() {
...
}
@Override
public void onListened(TalkContext talkContext, Primitive primitive) {
ActionDialect dialect = DialectFactory.getInstance().createActionDialect(primitive);
String action = dialect.getName();
if (action.equals("push")) {
// 接收到 Push 指令
this.executor.execute(new PushTask(this, talkContext, primitive));
}
else if (action.equals("pull")) {
// 接收到 Pull 指令
this.executor.execute(new PullTask(this, talkContext, primitive));
}
}
}
在 MessagingCellet 中,我们接收 push 和 pull 两个动作,并使用并发执行器分别执行 PushTask 和 PullTask 任务。
public class PushTask extends ServiceTask {
public PushTask(Cellet cellet, TalkContext talkContext, Primitive primitive) {
super(cellet, talkContext, primitive);
}
@Override
public void run() {
ActionDialect action = DialectFactory.getInstance().createActionDialect(this.primitive);
// 创建 Packet
Packet packet = new Packet(action);
// 从 Packet 负载里获取消息
Message message = new Message(packet);
// 生成消息副本 - 生成 From 的副本
Message fromCopy = new Message(message);
fromCopy.setOwner(message.getFrom());
// 生成消息副本 - 生成 To 的副本
Message toCopy = new Message(message);
toCopy.setOwner(message.getTo());
// 将消息写入时序缓存 - 写入 From 缓存
messageCache.add(fromCopy.getOwner(), fromCopy);
// 将消息写入时序缓存 - 写入 To 缓存
messageCache.add(toCopy.getOwner(), toCopy);
// 将消息发到实时队列,服务器通过监听方式将消息推送给 To 和 From 的其他设备
contactEventQueue.publish(toCopy.getOwner(), toCopy);
contactEventQueue.publish(fromCopy.getOwner(), fromCopy);
}
}
完整的服务器代码可以在 Gitee 或者 GitHub 上找到。
这里我们仅关注消息数据的流转过程。在消息的处理过程里,我们为一个消息生成了两个副本,并将两个副本分别保存,两个副本将通过全局队列发送给对应的仿比心一对一直播软件源码服务器,以便连接到这些服务器上的客户端接收到消息。因此,消息发送人(From)的其他设备也会接收到自己来自其他设备的消息,从而实现多设备消息的同步。
编写客户端程序
网络通信实现
在 Web 版的客户端里我们将使用 WebSocket 作为通信协议的实现,我们先创建一个 Pipeline 对象,Pipeline 作为网络数据通道,客户端的数据发送和接收都通过 Pipeline 来操作:
class Pipeline extends cell.TalkListener {
constructor() {
this.nucleus = new cell.Nucleus();
this.nucleus.talkService.addListener(this);
this.listener = [];
}
...
/** 发送数据到服务器 */
send(packet, handleResponse) {
let primitive = packet.toPrimitive();
this.nucleus.talkService.speak('Messaging', primitive);
}
/** 添加接收数据监听器 */
addListener(listener) {
this.listener.push(listener);
}
/** 收到消息时的回调 */
onListened(speaker, cellet, primitive) {
this.listener.forEach((item) => {
item(primitive);
});
}
}
我们使用了 cell.Nucleus 来管理 WebSocket 连接,cell 库可以从 cell-javascript 下载。
消息程序代码
我们先定义消息服务对象作为消息处理的入口对象:
class MessagingService {
constructor() {
this.pipeline = new Pipeline();
}
/** 启动服务。 */
start() {
this.pipeline.addListener((primitive) => {
this.onListened(primitive);
});
}
/** 停止服务。 */
stop() {
...
}
/** 发送消息。 */
sendTo(message) {
let packet = new Packet('push', message.toJSON());
this.pipeline.send(packet);
}
onListened(primitive) {
// 将网络层的数据结构转为 Packet 结构
let packet = new Packet(primitive);
if (packet.name == 'notify') {
// 接收到新消息
...
}
}
}
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>即时消息客户端</title>
</head>
<body>
<script src="js/cell.js"></script>
<script src="js/Pipeline.js"></script>
<script src="js/MessagingService.js"></script>
<script type="text/javascript">
let service = new MessagingService();
service.start();
// 发送人 ID 10000 ,接收人 ID 20000
let message = new Message(10000, 20000, 'Hello! This is a message');
// 发送消息
service.sendTo(message);
</script>
</body>
</html>
客户端的完整代码可以从 Gitee 或者 GitHub 上下载。
至此我们就实现了仿比心一对一直播软件源码的基本的消息收发功能。但是,在实际项目中对于消息的操作和管理远比上述的演示程序复杂。如果你从 Gitee 或者 GitHub 下载代码并阅读,会发现项目中的代码还需要处理消息超时、消息状态、消息时间戳等问题。而真正应用在仿比心一对一直播软件源码里时,还需要对消息的内容进行必要的风险管理,以便识别有风险的消息内容。