Netty-单聊和群聊

26 篇文章 1 订阅
  • 单聊 

单聊流程

 

public class SessionUtil {
    // userId -> channel 的映射
    private static final Map<String, Channel> userIdChannelMap = new ConcurrentHashMap<>();


    public static void bindSession(Session session, Channel channel) {
        userIdChannelMap.put(session.getUserId(), channel);
        channel.attr(Attributes.SESSION).set(session);
    }

    public static void unBindSession(Channel channel) {
        if (hasLogin(channel)) {
            userIdChannelMap.remove(getSession(channel).getUserId());
            channel.attr(Attributes.SESSION).set(null);
        }
    }
    
    public static boolean hasLogin(Channel channel) {

        return channel.hasAttr(Attributes.SESSION);
    }

    public static Session getSession(Channel channel) {

        return channel.attr(Attributes.SESSION).get();
    }

    public static Channel getChannel(String userId) {

        return userIdChannelMap.get(userId);
    }
}

主要的代码逻辑就是,当客户端发来消息的时候,拿到消息发送方的会话信息,通过消息发送方的会话信息构造要发送的消息,拿到消息接收方的 channel,将消息发送给消息接收方,就是这么简单

  1. SessionUtil 里面维持了一个 useId -> channel 的映射 map,调用 bindSession() 方法的时候,在 map 里面保存这个映射关系,SessionUtil 还提供了 getChannel() 方法,这样就可以通过 userId 拿到对应的 channel。
  2. 除了在 map 里面维持映射关系之外,在 bindSession() 方法中,我们还给 channel 附上了一个属性,这个属性就是当前用户的 Session,我们也提供了 getSession() 方法,非常方便地拿到对应 channel 的会话信息。
  3. 这里的 SessionUtil 其实就是前面小节的 LoginUtil,这里重构了一下,其中 hasLogin() 方法,只需要判断当前是否有用户的会话信息即可。
  4. 在 LoginRequestHandler 中,我们还重写了 channelInactive() 方法,用户下线之后,我们需要在内存里面自动删除 userId 到 channel 的映射关系,这是通过调用 SessionUtil.unBindSession() 来实现的。
  •  群聊 

如上图,要实现群聊,其实和单聊类似

  1. A,B,C 依然会经历登录流程,服务端保存用户标识对应的 TCP 连接
  2. A 发起群聊的时候,将 A,B,C 的标识发送至服务端,服务端拿到之后建立一个群聊 ID,然后把这个 ID 与 A,B,C 的标识绑定
  3. 群聊里面任意一方在群里聊天的时候,将群聊 ID 发送至服务端,服务端拿到群聊 ID 之后,取出对应的用户标识,遍历用户标识对应的 TCP 连接,就可以将消息发送至每一个群聊成员

关键代码

public class CreateGroupRequestHandler extends SimpleChannelInboundHandler<CreateGroupRequestPacket> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, CreateGroupRequestPacket createGroupRequestPacket) {
        List<String> userIdList = createGroupRequestPacket.getUserIdList();

        List<String> userNameList = new ArrayList<>();
        // 1. 创建一个 channel 分组
        ChannelGroup channelGroup = new DefaultChannelGroup(ctx.executor());

        // 2. 筛选出待加入群聊的用户的 channel 和 userName
        for (String userId : userIdList) {
            Channel channel = SessionUtil.getChannel(userId);
            if (channel != null) {
                channelGroup.add(channel);
                userNameList.add(SessionUtil.getSession(channel).getUserName());
            }
        }

        // 3. 创建群聊创建结果的响应
        CreateGroupResponsePacket createGroupResponsePacket = new CreateGroupResponsePacket();
        createGroupResponsePacket.setSuccess(true);
        createGroupResponsePacket.setGroupId(IDUtil.randomId());
        createGroupResponsePacket.setUserNameList(userNameList);

        // 4. 给每个客户端发送拉群通知
        channelGroup.writeAndFlush(createGroupResponsePacket);

        System.out.print("群创建成功,id 为[" + createGroupResponsePacket.getGroupId() + "], ");
        System.out.println("群里面有:" + createGroupResponsePacket.getUserNameList());

    }
}
  1. 首先,我们这里创建一个 ChannelGroup。这里简单介绍一下 ChannelGroup:它可以把多个 chanel 的操作聚合在一起,可以往它里面添加删除 channel,可以进行 channel 的批量读写,关闭等操作,详细的功能读者可以自行翻看这个接口的方法。这里我们一个群组其实就是一个 channel 的分组集合,使用 ChannelGroup 非常方便。
  2. 接下来,我们遍历待加入群聊的 userId,如果存在该用户,就把对应的 channel 添加到 ChannelGroup 中,用户昵称也添加到昵称列表中。
  3. 然后,我们创建一个创建群聊响应的对象,其中 groupId 是随机生成的,群聊创建结果一共三个字段,这里就不展开对这个类进行说明了。
  4. 最后,我们调用 ChannelGroup 的聚合发送功能,将拉群的通知批量地发送到客户端,接着在服务端控制台打印创建群聊成功的信息,至此,服务端处理创建群聊请求的逻辑结束。
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值