IM系统第五章 – 用户与群组通信
你是如何完成这个功能的?
实现该功能的流程是:
用户与群组的好友聊天,前提也是,你已经点开了相应的对话框(上两节已经实现了);这个群组的设置是一登陆就有的,也就说是默认的。实现逻辑就是:将群里的用户的channel
管道全部放到ChannelGroup
中进行群发消息,只要在这个通信管道里的用户都能收到消息。
上述描述转换为逻辑图如下(参照 小傅哥)
实现功能所需的处理:
- 自定义协议;
- UI事件实现;
- 服务端与群组通信功能处理;
- 客户端与群组通信功能处理;
自定义通信协议:MsgGroupRequest
、MsgGroupResponse
// 群组消息请求 协议
public class MsgGroupRequest extends Packet {
private String userId; // 群员ID
private String talkId; // 对话框ID
private String msgText; // 消息内容
private Integer msgType; // 消息类型
private Date msgDate; // 消息时间
构造函数...
get()/set()...
}
// 群组消息请求应答 协议
public class MsgGroupResponse extends Packet {
private String talkId; // 对话框ID
private String userId; // 群员ID
private String userNickName;// 群员用户昵称
private String userHead; // 群员用户头像
private String msgText; // 消息内容
private Date msgDate; // 消息时间
private Integer msgType; // 消息类型 表情包/文字内容
构造函数...
get()/set()...
}
自定义协议完成后,接下来还是和好友通信一样,完成UI事件的定义
public class ChatEvent implements IChatEvent {
/**
* 消息发送
*
* @param userId 用户Id
* @param talkId 对话Id(好友ID/群组ID)
* @param talkType 对话框类型;0好友、1群组
* @param msg 发送消息内容
* @param msgType 消息类型;0文字消息、1固定表情
* @param msgDate 发送消息时间
*/
@Override
public void doSendMsg(String userId, String talkId, Integer talkType, String msg, Integer msgType, Date msgDate) {
Channel channel = BeanUtil.getBean("channle", Channel.class);
if(talkType == 0) {
channel.writeAndFlush(new MsgRequest(userId, talkId, msg, msgType, msgDate));
}
if(talkType == 1) {
channel.writeAndFlush(new MsgGroupRequest(userId, talkId, msg, msgType, msgDate));
}
}
}
接下来是客户端的处理 消息请求应答 协议的内容,调用UI事件的接口来完成处理;
public class MsgGroupHandler extends MyBizHandler<MsgGroupResponse> {
@Override
public void channelRead(Channel channel, MsgGroupResponse msg) {
IChatMethod chat = uiService.getChat();
Platform.runLater(() -> {
/**
填充对话框消息 - 群组[别人发来的消息]
* @param talkId 对话框ID[群组ID]
* @param userId 用户ID[群员]
* @param userNickName 用户昵称
* @param userHead 用户头像
* @param msg 消息
* @param msgType 消息类型;0文字消息、1固定表情
* @param msgDate 时间
* @param idxFirst 是否设置首位
* @param selected 是否选中
* @param isRemind 是否提醒
*/
chat.addTalkMsgGroupLeft(msg.getTalkId(), msg.getUserId(), msg.getUserNickName(), msg.getUserHead(), msg.getMsg(), msg.getMsgType(), msg.getMsgDate(), true, false, true);
});
}
}
接下来就是服务端处理 消息请求 协议的内容了,服务端负责的内容是:因为前面讲到,需要将群组的用户的channel
收集起来放在channelGroup
中,所以首先获取群组管道;然后开始异步存储消息(上一节说过了),然后将信息发在channelGroup
中,由channelGroup
分发给各个用户的channel
即可;代码如下:
// 消息群组处理器(服务端)
public class MsgGroupHandler extends MyBizHandler<MsgGroupRequest> {
@Override
public void channelRead(Channel channel, MsgGroupRequest msg) {
// 获取群组消息通信管道
ChannelGroup channelGroup = SocketChannelUtil.getChannelGroup(msg.getTalkId());
if (null == channelGroup) { // 如果管道为空
// 添加 群组管道 在缓存中
SocketChannelUtil.addChannelGroup(msg.getTalkId(), channel);
// 通过 请求协议中的对话框类型 在缓存中获取到群组管道
channelGroup = SocketChannelUtil.getChannelGroup(msg.getTalkId());
}
// 将消息利用多线程的方式异步落库
userService.asyncAppendChatRecord(new ChatRecordInfo(msg.getUserId(), msg.getTalkId(), msg.getMsgText(), msg.getMsgType(), msg.getMsgDate(), Constants.TalkType.Group.getCode()));
// 根据消息协议的用户id获取每个用户传输对象
UserInfo userInfo = userService.queryUserInfo(msg.getUserId());
MsgGroupResponse msgGroupResponse = new MsgGroupResponse();
msgGroupResponse.setTalkId(msg.getTalkId());
msgGroupResponse.setUserId(msg.getUserId());
msgGroupResponse.setUserNickName(userInfo.getUserNickName());
msgGroupResponse.setUserHead(userInfo.getUserHead());
msgGroupResponse.setMsg(msg.getMsgText());
msgGroupResponse.setMsgType(msg.getMsgType());
msgGroupResponse.setMsgDate(msg.getMsgDate());
channelGroup.writeAndFlush(msgGroupResponse);
}
}
到了这个之后和 用户与好友通信部分就没什么不一样了(且将消息异步落库也是和前一章的内容相同);也是操作mybatis
的将数据放入到数据库的过程。。。
总结
这里只需要提出一点即可:
和 用户与好友通信一节最大的不同是:在服务端的处理上,需要将群组管道channelGroup
获取到,然后将 群组消息请求应答 协议(MsgGroupResponse
)的内容包装传给channelGroup
,让其分发给每个用户的channel
即可。