Chinaunix首页 | 论坛 | 博客
  • 博客访问: 105836
  • 博文数量: 228
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 2290
  • 用 户 组: 普通用户
  • 注册时间: 2021-05-18 15:26
文章分类

全部博文(228)

文章存档

2023年(40)

2022年(114)

2021年(74)

我的朋友

分类: 云计算

2023-03-21 14:00:51

群聊的大概流程就是:根据群组 ID 查找到所有的成员集合,然后再遍历找到每个成员对应的连接通道。

群聊通讯流程技术原理如下:
    1)群聊和单聊整体上的思路一致:需要保存每个用户和通道的对应关系,方便后期通过用户 ID 去查找到对应的通道,再跟进通道推送消息;2)群聊把消息发送给群员的原理:其实很简单,服务端再保存另外一份映射关系,那就是聊天室和成员的映射关系。发送消息时,首先根据聊天室 ID 找到对应的所有成员,然后再跟进各个成员的 ID 去查找到对应的通道,{BANNED}最佳后由每个通道进行消息的发送;3)群成员加入某个群聊聊的时候:往映射表新增一条记录,如果成员退群的时候则删除对应的映射记录。

服务端实体
服务端映射关系的管理,分别是:
    1)登录信息(用户 ID 和通道);2)群组信息(群组 ID 和群组成员关系)。

主要通过两个 Map 去维护,具体如下:
public class ServerChatGroupHandler extends ChannelInboundHandlerAdapter {
private static Map map=new HashMap();
private static Map groups=new HashMap();
}
//组和成员列表关系实体
@Data
public class Group implements Serializable {
private String groupName;
private List members=new ArrayList();
}即时通讯聊天软件app开发可以加小蓝豆的v:weikeyun24咨询

//成员和连接通道的关系实体
public class GroupMember implements Serializable {
private Integer userid;
private Channel channel;
}

我们准备好相应的实体,以及实体和指令的映射关系,具体如下所示:
private static Map> map=new HashMap>();
static{
//登录的请求和响应实体
map.put(1, LoginReqBean.class);
map.put(2, LoginResBean.class);

//创建群组的请求和响应实体
map.put(3, GroupCreateReqBean.class);
map.put(4, GroupCreateResBean.class);

//查看群组的请求和响应实体
map.put(5, GroupListReqBean.class);
map.put(6, GroupListResBean.class);

//加入群组的请求和响应实体
map.put(7,GroupAddReqBean.class);
map.put(8,GroupAddResBean.class);

//退出群组的请求和响应实体
map.put(9,GroupQuitReqBean.class);
map.put(10,GroupQuitResBean.class);

//查看成员列表的请求和响应实体
map.put(11,GroupMemberReqBean.class);
map.put(12,GroupMemberResBean.class);

//发送响应的实体(发送消息、发送响应、接受消息)
map.put(13,GroupSendMsgReqBean.class);
map.put(14,GroupSendMsgResBean.class);
map.put(15,GroupRecMsgBean.class);
}

IM群聊功能的实现,我们需要两个两个业务 Handler:
    1)分别是客户端(ClientChatGroupHandler);2)服务端(ServerChatGroupHandler)。

客户端 Handler


客户端 Handler,主要是通过判断实体类型来做不同的业务操作,当然也可以使用 SimpleChannelInboundHandler 去进行 Handler 拆分。


public class ClientChatGroupHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//在链接就绪时登录
login(ctx.channel());
}

//主要是“接受服务端”的响应信息
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if(msg instanceof LoginResBean){
LoginResBean res=(LoginResBean) msg;
System.out.println("登录响应:"+res.getMsg());
if(res.getStatus()==0){
//登录成功

//1.给通道绑定身份
ctx.channel().attr(AttributeKey.valueOf("userid")).set(res.getUserid());

//2.显示操作类型【请看下面】
deal(ctx.channel());
}else{
//登录失败,继续登录
login(ctx.channel());
}
}else if(msg instanceof GroupCreateResBean){
GroupCreateResBean res=(GroupCreateResBean)msg;
System.out.println("创建响应群组:"+res.getMsg());

}else if(msg instanceofGroupListResBean){
GroupListResBean res=(GroupListResBean)msg;
System.out.println("查看群组列表:"+res.getLists());

}elseif(msg instanceofGroupAddResBean){
GroupAddResBean res=(GroupAddResBean)msg;
System.out.println("加入群组响应:"+res.getMsg());

}elseif(msg instanceof GroupQuitResBean){
GroupQuitResBean res=(GroupQuitResBean)msg;
System.out.println("退群群组响应:"+res.getMsg());

}else if(msg instanceof GroupMemberResBean){
GroupMemberResBean res=(GroupMemberResBean)msg;
if(res.getCode()==1){
System.out.println("查看成员列表:"+res.getMsg());
}else{
System.out.println("查看成员列表:"+res.getLists());
}

}else if(msg instanceof GroupSendMsgResBean){
GroupSendMsgResBean res=(GroupSendMsgResBean)msg;
System.out.println("群发消息响应:"+res.getMsg());

}else if(msg instanceof GroupRecMsgBean){
GroupRecMsgBean res=(GroupRecMsgBean)msg;
System.out.println("收到消息fromuserid="+
res.getFromuserid()+
",msg="+res.getMsg());
}
}
}


通过子线程循环向输出控制台输出操作类型的方法,以下方法目前都是空方法,下面将详细讲解。


private void deal(final Channel channel){
final Scanner scanner=new Scanner();
new Thread(new Runnable() {
public void run() {
while(true){
System.out.println("请选择类型:0创建群组,1查看群组,2加入群组,3退出群组,4查看群成员,5群发消息");
int type=scanner.nextInt();
switch(type){
case 0:
createGroup(scanner,channel);
break;
case 1:
listGroup(scanner,channel);
break;
case 2:
addGroup(scanner,channel);
break;
case 3:
quitGroup(scanner,channel);
break;
case 4:
listMembers(scanner,channel);
break;
case 5:
sendMsgToGroup(scanner,channel);
break;
default:
System.out.println("输入的类型不存在!");
}
}
}
}).start();
}


阅读(266) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~