在CometD 中,您可以使用两种方法将列表广播给客户端:
- 每次更改时发送整个列表
- 在客户端首次连接时发送整个列表,然后只发送更改
第一种方法适用于小列表,第二种方法适用于较大的列表。
我将讨论第二种方法,因为所使用的技术也适用于第一种方法。
作为使代码 sn-ps 更清晰的示例,假设您要通过频道 /room1/members 发送聊天室成员列表。
在订阅时发送整个列表
在您的房间配置代码中,您向会员频道注册了SubscriptionListener:
private void configureRoom(String roomName) {
final String channelName = "/" + roomName + "/members";
ServerChannel channel = bayeuxServer.createChannelIfAbsent(channelName).getReference();
channel.addListener(new SubscriptionListener() {
@Override
public void subscribed(ServerSession session, ServerChannel channel, ServerMessage message) {
// Send back the whole list to the subscriber.
session.deliver(sender, channelName, memberList);
}
});
请注意,您想在这里使用ServerSession.deliver() 将整个列表发送给该订阅者:其他订阅者已经拥有该列表,因此无需将其广播给所有订阅者。
发送列表更改
当列表发生变化时,您可以只广播变化,例如当成员离开聊天室时:
private void memberLeft(String roomName, String memberName) {
int index = memberList.indexOf(memberName);
if (index < 0) {
return;
}
memberList.remove(index);
Map<String, Object> data = new HashMap<>();
data.put("action", "remove");
data.put("index", index);
String channelName = "/" + roomName + "/members";
ServerChannel channel = bayeuxServer.getChannel(channelName);
channel.publish(sender, data);
}
请注意,您要在此处使用ServerChannel.publish() 将更改广播给所有订阅者。
客户端将看到两种类型的消息到达通道/room1/members:一种是数据是字符串列表 - 然后它会知道它是整个列表,另一种是数据是对象 - 然后它会知道这是一个列表更改。
对于后者,数据将有一个action 字段告诉您需要执行什么操作,还有一个index 字段告诉您需要在哪个索引处操作。
这两条消息的格式完全取决于您。
应对网络中断
当客户端暂时离线时,服务器可能会发送列表更改。对于这种情况,CometD 提供了message acknowledgement extension,它提供了服务器到客户端保证的消息传递。
对于较长的离线时间,客户端将与服务器重新握手,因此它将在重新订阅时获得完整列表。
请遵循this section 的CometD documentation 中描述的指南。