Netty入门P14

添加登录验证功能

登录验证处理器示例代码:

/**
 * @program: learnnetty
 * @description: 服务端验证客户端身份
 * @create: 2020-05-11 15:55
 **/
public class ServerAuthHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (!LoginUtil.hasLogin(ctx.channel())){
            ctx.channel().close();
        }else {
            //如果已经登录则不需要每次都验证
            ctx.pipeline().remove(this);
            super.channelRead(ctx, msg);
        }
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        if (LoginUtil.hasLogin(ctx.channel())){
            System.out.println("已完成登录验证,无需再次验证");
        }else {
            System.out.println("未登录验证,关闭连接");
        }
    }
}

在第一次登录之后就可以把此处的验证移除,同一条链接发消息没必要每一次都验证一下。

为服务端添加登录验证处理器:

/**
 * @program: learnnetty
 * @description: 服务端
 * @create: 2020-05-06 17:18
 **/
public class Server {

    public static void main(String[] args) {
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();

        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap
                .group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
                        nioSocketChannel
                                .pipeline()
                                .addLast(new FrameDecoder())
                                .addLast(new ServerLoginHandler())
                                .addLast(new ServerAuthHandler())
                                .addLast(new ServerMsgHandler())
                                .addLast(new FrameEncoder());
                    }
                });

        bind(serverBootstrap, 8080);
    }

    private static void bind(final ServerBootstrap serverBootstrap, final int port){
        serverBootstrap.bind(port).addListener(new GenericFutureListener<Future<? super Void>>() {
            @Override
            public void operationComplete(Future<? super Void> future) throws Exception {
                if (future.isSuccess()){
                    System.out.println("端口[" + port + "]绑定成功");
                }else {
                    System.out.println("端口[" + port + "]绑定失败,尝试绑定[" + (port + 1) + "]端口");
                    bind(serverBootstrap, port + 1);
                }
            }
        });
    }
}

当连接被拒绝时客户端应该有反馈:

/**
 * @program: learnnetty
 * @description: 客户端业务处理
 * @create: 2020-05-06 15:40
 **/
public class ClientLoginHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println(new Date() + ",客户端开始登录");

        LoginRequestFrame loginFrame = new LoginRequestFrame();
        loginFrame.setUserId(UUID.randomUUID().toString());
        loginFrame.setUserName("zcd");
        loginFrame.setPassword("zzz");

        ctx.channel().writeAndFlush(loginFrame);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        BaseFrame frame = (BaseFrame) msg;

        if (frame instanceof LoginResponseFrame){
            LoginUtil.resolveRespLoginFrame(frame, ctx);
        }else {
            ctx.fireChannelRead(frame);
        }
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("未通过登录验证关闭连接");
    }
}

客户端成功连接服务端输出:

客户端连接成功
Mon May 11 16:16:33 CST 2020,客户端开始登录
客户端登录成功
输入消息发送至服务端:
123
输入消息发送至服务端:
Mon May 11 16:16:36 CST 2020,收到来自服务器的消息:服务器已收到消息
123
输入消息发送至服务端:
Mon May 11 16:16:38 CST 2020,收到来自服务器的消息:服务器已收到消息
123
输入消息发送至服务端:
Mon May 11 16:16:39 CST 2020,收到来自服务器的消息:服务器已收到消息

客户端多次发送消息至服务端输出:

端口[8080]绑定成功
Mon May 11 16:16:34 CST 2020,服务端开始处理请求
Mon May 11 16:16:34 CST 2020,zcd客户端登录成功
Mon May 11 16:16:36 CST 2020,服务端开始处理请求
已完成登录验证,无需再次验证
Mon May 11 16:16:36 CST 2020,收到来自客户端的消息:123
Mon May 11 16:16:38 CST 2020,服务端开始处理请求
Mon May 11 16:16:38 CST 2020,收到来自客户端的消息:123
Mon May 11 16:16:39 CST 2020,服务端开始处理请求
Mon May 11 16:16:39 CST 2020,收到来自客户端的消息:123

一对一的消息传递

解析来自客户端的信息:

public class ServerMsgHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        BaseFrame baseFrame = (BaseFrame) msg;
        if (baseFrame instanceof MsgRequestFrame){
            //ctx.channel().writeAndFlush(MsgUtil.respMsg(baseFrame));
            //声明响应实体
            BaseFrame respFrame;
            Channel aimUserChannel;
            //解析目标用户id
            String toId = ((MsgRequestFrame)baseFrame).getToUserId();
            //获取目标用户Channel
            if (SessionUtil.getChannel(toId) != null && LoginUtil.hasLogin(SessionUtil.getChannel(toId))){
                aimUserChannel = SessionUtil.getChannel(toId);
            }else if (SessionUtil.hasGroup(toId)){
                MsgUtil.postGroupMsg(SessionUtil.getChannels(toId), (MsgRequestFrame)baseFrame);
                return;
            }else {
                aimUserChannel = ctx.channel();
                baseFrame = new MsgResponseFrame();
                ((MsgResponseFrame)baseFrame).setContent("对方不在线");
            }
            //发送消息
            aimUserChannel.writeAndFlush(baseFrame);
        }else {
            ctx.fireChannelRead(baseFrame);
        }
    }

}

根据目标ID在SessionUtil中获取对应的Channel,并将数据从指定的Channel中发出。

加入群聊

解析来自客户端的命令,并作出反馈:

public class ServerOrderHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        BaseFrame baseFrame = (BaseFrame)msg;
        if (baseFrame instanceof OrderReqFrame){
            OrderReqFrame orderReqFrame = (OrderReqFrame)baseFrame;
            OrderRespFrame orderRespFrame = new OrderRespFrame();
            byte order = orderReqFrame.getOrder();
            switch (order){
                case Order.ALL_ONLINE_USERS:
                    orderRespFrame.setResult(ServerOnlineUserCache.getAllUser());
                    orderRespFrame.setFlag(Order.FLAG_POSITIVE);
                    break;
                case Order.ALL_GROUP:
                    orderRespFrame.setResult(SessionUtil.getAllGroup());
                    orderRespFrame.setFlag(Order.FLAG_POSITIVE);
                    break;
                case Order.CREATE_GROUP:
                    orderRespFrame.setResult(SessionUtil.createGroup(baseFrame, ctx.channel(), ctx));
                    orderRespFrame.setFlag(Order.FLAG_POSITIVE);
                    break;
                case Order.INTO_GROUP:
                    orderRespFrame.setResult(SessionUtil.insertGroup(baseFrame, ctx.channel()));
                    orderRespFrame.setFlag(Order.FLAG_POSITIVE);
                    break;
                case Order.LEAVE_GROUP:
                    orderRespFrame.setResult(SessionUtil.leaveGroup(baseFrame, ctx.channel()));
                    orderRespFrame.setFlag(Order.FLAG_POSITIVE);
                    break;
                case Order.ALL_INSERT_GROUP:
                    orderRespFrame.setResult(SessionUtil.selectChannelInGroup(baseFrame, ctx.channel()));
                    orderRespFrame.setFlag(Order.FLAG_POSITIVE);
                    break;
                default:
                    orderRespFrame.setResult("未知命令");
                    orderRespFrame.setFlag(Order.FLAG_NEGATIVE);
            }
            ctx.channel().writeAndFlush(orderRespFrame);
        }else {
            ctx.fireChannelRead(msg);
        }
    }
}

根据发送来的群号id,将该Channel加入到指定的ChannelGroup中:

public class SessionUtil {

    /**
     * 用户与Channel的映射
     */
    private static Map<String, Channel> sessions = new ConcurrentHashMap<>();

    /**
     * 群聊缓存Channel
     */
    private static Map<String, ChannelGroup> groupSession = new ConcurrentHashMap<>();

    /**
     * 群聊信息缓存
     */
    private static Map<String, String> groupInfo = new ConcurrentHashMap<>();

    public static String createGroup(BaseFrame baseFrame, Channel channel, ChannelHandlerContext ctx){
        String groupId = UUID.randomUUID().toString();
        OrderReqFrame orderReqFrame = (OrderReqFrame)baseFrame;
        ChannelGroup channels = new DefaultChannelGroup(ctx.executor());
        channels.add(channel);
        groupSession.put(groupId, channels);
        groupInfo.put(groupId, "[" + orderReqFrame.getUserName() + "的群聊]");
        return "群号[" + groupId + "],名称[" + orderReqFrame.getUserName() + "的群聊]建立完成";
    }

    public static String insertGroup(BaseFrame baseFrame, Channel channel){
        OrderReqFrame orderReqFrame = (OrderReqFrame)baseFrame;
        String groupId = (String)orderReqFrame.getContent();
        if (hasGroup(groupId)){
            groupSession.get(groupId).add(channel);
            return "群号[" + groupId + "],名称" + groupInfo.get(groupId) + ",加入成功";
        }else {
            return "群号[" + groupId +"]不存在";
        }
    }

    public static String leaveGroup(BaseFrame baseFrame, Channel channel){
        if (baseFrame == null){
            for (Map.Entry<String, ChannelGroup> cursor :
                    groupSession.entrySet()) {
                if (cursor.getValue().find(channel.id()) != null){
                    String groupId = cursor.getKey();
                    cursor.getValue().remove(channel);
                    if (cursor.getValue().isEmpty()){
                        groupSession.remove(groupId);
                        groupInfo.remove(groupId);
                    }
                }
            }
            return null;
        }
        OrderReqFrame orderReqFrame = (OrderReqFrame)baseFrame;
        String groupId = (String)orderReqFrame.getContent();
        if (groupSession.containsKey(groupId)){
            groupSession.get(groupId).remove(channel);
            if (groupSession.get(groupId).isEmpty()){
                groupSession.remove(groupId);
                groupInfo.remove(groupId);
            }
            return "群聊退出成功";
        }else {
            return "尚未加入该群聊";
        }
    }

    public static Object getAllGroup(){
        return groupInfo;
    }

    /**
     * 设置Channel的属性
     */
    public static void bindSession(Session session, Channel channel){
        sessions.put(session.getUserId(), channel);
        channel.attr(Attributes.SESSION).set(session);
    }

    /**
     * 移除属性
     */
    public static void unbindSession(Channel channel){
        if (hasLogin(channel)){
            ServerOnlineUserCache.removeUser(getSession(channel).getUserId());
            sessions.remove(getSession(channel).getUserId());
            channel.attr(Attributes.SESSION).set(null);
        }
        System.out.println(sessions);
    }

    /**
     * 获取所有已加入的群聊
     */
    public static Object selectChannelInGroup(BaseFrame baseFrame, Channel channel) {
        Map<String, String> result = new HashMap<>();
        for (Map.Entry<String, ChannelGroup> cursor : groupSession.entrySet()){
            if (cursor.getValue().find(channel.id()) != null){
                result.put(cursor.getKey(), groupInfo.get(cursor.getKey()));
            }
        }
        return result;
    }

    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 sessions.get(userId);
    }

    public static boolean hasGroup(String groupId){
        return groupSession.containsKey(groupId);
    }

    public static ChannelGroup getChannels(String groupId){
        return groupSession.get(groupId);
    }

    public static String getGroupInfo(String groupId){
        return groupInfo.get(groupId);
    }
}

群聊信息的发送

解析来自客户端发来的消息,如果目标ID不是用户那就在ChannelGroup的缓存中寻找:

public class ServerMsgHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        BaseFrame baseFrame = (BaseFrame) msg;
        if (baseFrame instanceof MsgRequestFrame){
            //ctx.channel().writeAndFlush(MsgUtil.respMsg(baseFrame));
            //声明响应实体
            BaseFrame respFrame;
            Channel aimUserChannel;
            //解析目标用户id
            String toId = ((MsgRequestFrame)baseFrame).getToUserId();
            //获取目标用户Channel
            if (SessionUtil.getChannel(toId) != null && LoginUtil.hasLogin(SessionUtil.getChannel(toId))){
                aimUserChannel = SessionUtil.getChannel(toId);
            }else if (SessionUtil.hasGroup(toId)){
                MsgUtil.postGroupMsg(SessionUtil.getChannels(toId), (MsgRequestFrame)baseFrame);
                return;
            }else {
                aimUserChannel = ctx.channel();
                baseFrame = new MsgResponseFrame();
                ((MsgResponseFrame)baseFrame).setContent("对方不在线");
            }
            //发送消息
            aimUserChannel.writeAndFlush(baseFrame);
        }else {
            ctx.fireChannelRead(baseFrame);
        }
    }

}

将发来的消息使用指定的ChannelGroup群发:

public class MsgUtil {

    /**
     * 对Msg做出响应
     */
    public static BaseFrame respMsg(BaseFrame baseFrame) {
        MsgRequestFrame msgRequestFrame = (MsgRequestFrame)baseFrame;
        System.out.println(new Date() + ",收到来自客户端的消息:" + msgRequestFrame.getContent());

        MsgResponseFrame responseFrame = new MsgResponseFrame();
        responseFrame.setContent("服务器已收到消息");
        return responseFrame;
    }

    /**
     * 发送请求Msg
     */
    public static BaseFrame reqMsg(String line){
        MsgRequestFrame requestFrame = new MsgRequestFrame();
        requestFrame.setContent(line);
        return requestFrame;
    }

    /**
     * 消息群发
     */
    public static void postGroupMsg(ChannelGroup channels, MsgRequestFrame msgRequestFrame){
        String groupId = msgRequestFrame.getToUserId();
        String groupName = SessionUtil.getGroupInfo(groupId);
        String posterId = msgRequestFrame.getFromUserId();
        String posterName = msgRequestFrame.getFromUserName();
        MsgRequestFrame requestFrame = new MsgRequestFrame();
        requestFrame.setGroupInfo(true);
        requestFrame.setFromUserName(groupName);
        requestFrame.setFromUserId(groupId);
        requestFrame.setContent("[" + posterId + "], " + posterName + ",对大家说:" + msgRequestFrame.getContent());
        channels.writeAndFlush(requestFrame);
    }

}

你可能感兴趣的:(学习笔记)