4. Netty+SpringBoot实现IM服务 之 用户与channel绑定

您所在的位置:网站首页 netty的channel可以放到redis中吗 4. Netty+SpringBoot实现IM服务 之 用户与channel绑定

4. Netty+SpringBoot实现IM服务 之 用户与channel绑定

2024-07-07 14:44| 来源: 网络整理| 查看: 265

系列文章目录 技术选型、简单实现16进制数据及解决半包和粘包拆包器与心跳检测用户与channel绑定通过Redis的订阅机制实现服务集群

授权登录 系列文章目录定义登录协议用户与channel绑定存储用户ID和channel的对应关系存储channel和用户ID的对应关系关键代码展示X.javaSocketHandlerAdapter 总结

定义登录协议

在这里插入图片描述 为了方便演示,我们简单的定义一下登录协议,协议的body就是用户的ID。

用户与channel绑定 存储用户ID和channel的对应关系 ConcurrentHashMap channelMap = new ConcurrentHashMap();

定义一个map实现用户ID和channel的绑定关系。这样通过用户的ID可以直接找到channel

存储channel和用户ID的对应关系 AttributeKey key = AttributeKey.valueOf("user"); channel.attr(key).set(userId);

使用netty提供的Attribute实现channel和用户ID的绑定。

关键代码展示 X.java import io.netty.channel.Channel; import io.netty.util.AttributeKey; import java.util.concurrent.ConcurrentHashMap; /** * @author Mr.Guo * @date 2021/3/9 下午4:15 */ public class X { public static final ConcurrentHashMap channelMap = new ConcurrentHashMap(); /** * 判断一个通道是否有用户在使用 * 可做信息转发时判断该通道是否合法 * * @param channel * @return */ public static boolean hasUser(Channel channel) { AttributeKey key = AttributeKey.valueOf("user"); //netty移除了这个map的remove方法,这里的判断谨慎一点 return (channel.hasAttr(key) || channel.attr(key).get() != null); } /** * 上线一个用户 * * @param channel * @param userId */ public static void online(Channel channel, String userId) { //先判断用户是否在web系统中登录? //这部分代码个人实现 channelMap.put(userId, channel); AttributeKey key = AttributeKey.valueOf("user"); channel.attr(key).set(userId); } /** * 根据用户id获取该用户的通道 * * @param userId * @return */ public static Channel getChannelByUserId(String userId) { return channelMap.get(userId); } /** * 判断一个用户是否在线 * * @param userId * @return */ public static Boolean online(String userId) { return channelMap.containsKey(userId) && channelMap.get(userId) != null; } } SocketHandlerAdapter import cn.xa87.im.demo.X; import cn.xa87.im.demo.packet.Xa87Packet; import com.alibaba.fastjson.JSON; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.timeout.IdleState; import io.netty.handler.timeout.IdleStateEvent; import io.netty.util.Attribute; import io.netty.util.AttributeKey; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; /** * @author Mr.Guo * @date 2021/3/4 下午5:03 */ @Slf4j @Component @ChannelHandler.Sharable public class SocketHandlerAdapter extends ChannelHandlerAdapter { @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { cause.printStackTrace(); ctx.close(); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { Xa87Packet packet = (Xa87Packet) msg; String body = new String(packet.getBody()); log.info("收到信息:{}", JSON.toJSONString(msg)); log.info("body==>{}", body); if (packet.getCommand() == 1) { X.online(ctx.channel(), body); } ctx.writeAndFlush(msg); } @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if (evt instanceof IdleStateEvent) { IdleState state = ((IdleStateEvent) evt).state(); if (state == IdleState.READER_IDLE) { // 在规定时间内没有收到客户端的上行数据, 主动断开连接 log.info("{}超时,断开连接", ctx.channel().id()); ctx.close(); } } else { super.userEventTriggered(ctx, evt); } } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { log.info("channelInactive"); Attribute attr = ctx.attr(AttributeKey.valueOf("user")); if (null != attr && null != attr.get()) { X.channelMap.remove(attr.get()); attr.remove(); } super.channelInactive(ctx); } } 总结

到此一个简单的IM系统已经基本搭建起来了。之后就是实现具体的业务逻辑了。 我把代码分享给大家 : 项目地址



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3