Netty-Server-Hander自定义解码器-服务端主动推送

netty server端使用自定义解码器,通过存储client连接实现主动推送消息,并发送自定义心跳包

Server端

依赖
		<dependency>
			<groupId>io.netty</groupId>
			<artifactId>netty-all</artifactId>
			<version>4.1.19.Final</version>
		</dependency>
创建服务端
@Component
public class NettyServer {

    @Autowired
    private ServiceConfiguration serviceConfiguration;

    private EventLoopGroup bossGroup = new NioEventLoopGroup();
    private EventLoopGroup workGroup = new NioEventLoopGroup();

    /**
     * 启动netty服务
     *
     * @throws InterruptedException
     */
    @PostConstruct
    public void start() throws Exception {

        ServerBootstrap b = new ServerBootstrap();
        b.group(bossGroup, workGroup)
                .channel(NioServerSocketChannel.class)
                //SO_BACKLOG只能控制最大并发量,无法限制最大连接数
                .option(ChannelOption.SO_BACKLOG, 128)
                //端口复用
                .option(ChannelOption.SO_REUSEADDR, true)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        //执行顺序为注册顺序的逆序
                        //IdleStateHandler()中的参数为多长时间未读,未写,(未读且未写)触发事件
                        socketChannel.pipeline().addLast(new IdleStateHandler(0, 0, 5, TimeUnit.SECONDS));
                        socketChannel.pipeline().addLast("messageHandler", new MessageHandler());
                        socketChannel.pipeline().addLast("serverHandler", new ServerHandler());
                    }
                });
        //开启需要监听 的端口
        ChannelFuture future = b.bind(serviceConfiguration.getAddress(), serviceConfiguration.getClientPort()).sync();
        if (future.isSuccess()) {
        	//serviceConfiguration类通过@value引入可配置的IP和端口
            System.out.println("IP:" + serviceConfiguration.getAddress() + "端口" + serviceConfiguration.getClientPort() + "启动监听成功");
        }
    }

    /**
     * 销毁
     */
    @PreDestroy
    public void destroy() {
        //syncUninterruptibly()主线程同步等待子线程结果,子线程释放后关闭
        bossGroup.shutdownGracefully().syncUninterruptibly();
        workGroup.shutdownGracefully().syncUninterruptibly();
        System.out.println("关闭 Netty 成功");
    }

ServerHandler
@Component
public class ServerHandler extends ChannelInboundHandlerAdapter {

    @Autowired
    private ServiceConfiguration serviceConfiguration;

    /**
     * 获取真实连接
     */
    public static Map<String, ChannelHandlerContext> realMap = new ConcurrentHashMap<>();


    /**
     * 获取到连接时触发
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {

		//监测到连接时先发送一个自定义心跳信息
		//通过发送自定义心跳的方法做身份验证,保存真实连接
        sendHeart(ctx);

    }

    /**
     * 断开连接触发
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {

        System.out.println("Disconnected client." + ctx.channel().remoteAddress().toString());
        //检测到连接关闭时移除存储的key信息
        String ipAddress = getContext(ctx);
        if (realMap.containsKey(ipAddress)) {
            realMap.remove(ipAddress);
        }
        ctx.fireChannelInactive();
    }


    /**
     * 获取数据
     *
     * @param ctx 上下文
     * @param msg 获取的数据
     * @throws UnsupportedEncodingException
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws UnsupportedEncodingException {
        
		//接收到解码器返回的数据后先进行判断
		//无特殊需求可直接通过ctx来处理数据
        if (realMap.size() > 1) {
            //除非被恶意攻击,理论上此处不会发生。。。
            realMap.clear();
            //发送身份验证包
            sendHeart(ctx);
        } else {
       		//解析数据
            byte[] bytes = (byte[]) msg;
            String s = getMessage(bytes);
            if (s.contains("heartbeats")) {
            	//处理心跳数据
                System.out.println(s);
                HeartBeat heartBeat = JsonUtil.json2Object(s, HeartBeat.class);
                System.out.println(heartBeat);
                System.out.println("接收到的数据" + ByteCopyUtil.toHexString(bytes));
                if(realMap.size() != 1){
                	//存储连接
                    realMap.put(getContext(ctx), ctx);
                }
            } else {
                //处理其他格式包的业务逻辑
                System.out.println("接收到的数据" + ByteCopyUtil.toHexString(bytes));
            }

        }
    }

	/*
	 *异常关闭连接
	 */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
	
	/*
	 * 通过IdleStateHandler()定时发送自定义心跳
	 */
    @Override
    public void userEventTriggered(ChannelHandlerContext context, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleState state = ((IdleStateEvent) evt).state();
            //IdleState.XX 处理事件
            if (state == IdleState.ALL_IDLE) {
                sendHeart(context);
            }
        }
    }

	/**
     * 获取客户端IP地址
     */
    public String getContext(ChannelHandlerContext context) {
        InetSocketAddress ipSocket = (InetSocketAddress) context.channel().remoteAddress();
        String clientIp = ipSocket.getAddress().getHostAddress();
        System.out.println("客户端ip地址" + clientIp);
        return clientIp;
    }
    
	/**
     * 自定义发送消息方法
     */
    public int sendMessage(String jsonObj) {
		//根据连接数量返回异常状态值
        if (realMap.size() == 1) {
            realMap.get(serviceConfiguration.getAddress()).writeAndFlush(SendJsonObj.getBuf(jsonObj));
            return 1;
        } else if (realMap.size() > 1) {
        	//连接数量大于1,拒绝发送数据
            return -1;
        } else {
        	//未获取到真实连接
            return 0;
        }
    }
    
   /*
    *通用发送方法
    */
   public void sendMessageData(String ipAddress,String jsonObj) {
		//通过ip地址指定要发送客户端
        realMap.get(ipAddress).writeAndFlush(SendJsonObj.getBuf(jsonObj));   
    }

    public void sendHeart(ChannelHandlerContext context) {
    	//HeartBeat 自定义心跳类
        HeartBeat heartBeat = new HeartBeat("heartbeats");
        //以json的形式发送
        String s = JsonUtil.object2Json(heartBeat);
        //获取连接主动发送给对端身份验证信息(SendJsonObj类中自定义了一个身份校验包(ByteBuf))
        context.writeAndFlush(SendJsonObj.getBuf(s));
    }

	/*
	 *解析获取自定义包中Json字符串长度
	 */
    public int getMessageLen(byte[] bytes) {
        if (bytes.length > 8) {
            byte[] len = new byte[4];
            len[0] = bytes[4];
            len[1] = bytes[5];
            len[2] = bytes[6];
            len[3] = bytes[7];
            return BitConverter.toInt(len) - 17;
        } else {
            return -1;
        }
    }

	/*
	 *解析自定义包中的Json字符串
	 */
    public String getMessage(byte[] bytes) {
        int len = getMessageLen(bytes);
        byte[] message = new byte[len];
        try {
            for (int i = 0; i < len; i++) {
                message[i] = bytes[i + 16];
            }
            String s = JsonUtil.object2Json(new String(message));
            return s;
        } catch (Exception e) {
            e.printStackTrace();
            return "getMessageFail";
        }

    }
MessageHandler(自定义解码器)
@Component
public class MessageHandler extends ByteToMessageDecoder {

    private ByteBuf tempMessage = Unpooled.buffer();

    public MessageHandler() {
    	//自定义包头
        this.tempMessage.writeInt(0x00000000);
    }

	/*
	 *找到并返回一个完整包
	 */
    @Override
    protected void decode(ChannelHandlerContext context, ByteBuf input, List<Object> output) throws Exception {
        while (true) {
        
            int index = IndexOf(input, tempMessage);
            if (index == -1) {
                //没找到
                return;
            } else if (index != 0) {
                //找到了
                input.skipBytes(index);
            }
            if (input.readableBytes() < 8) {
                return;
            }
            input.markReaderIndex();
            int length = 0;

            ByteBuf b = input.skipBytes(4).readBytes(4);

            byte[] bs = new byte[b.readableBytes()];
            b.readBytes(bs);

            b.release();
            length = BitConverter.ToInt32(bs, 0);

            input.resetReaderIndex();
            //异常处理
            if (length < 0) {
                input.skipBytes(4);
                return;
            }

            //length长度为协议长度,不包括包头或校验位等位置
            if (input.readableBytes() >= (length)) {
                byte[] bb = new byte[length];
                input.readBytes(bb);
                output.add(bb);
            } else {
                return;
            }
        }

    }

	/*
	 *查找自定义包头
	 */
    static int IndexOf(ByteBuf haystack, ByteBuf needle) {
        for (int i = haystack.readerIndex(); i < needle.writerIndex(); i++) {
            int haystackIndex = i;
            int needleIndex;
            for (needleIndex = 0; needleIndex < needle.capacity(); needleIndex++) {
                if (haystack.getByte(haystackIndex) != needle.getByte(needleIndex)) {
                    break;
                } else {
                    haystackIndex++;
                    if (haystackIndex == haystack.writerIndex() && needleIndex != 3) {
                        return -1;
                    }
                }
            }
            if (needleIndex == 4) {
                return i - haystack.readerIndex();
            }
        }
        return -1;
    }

发布了6 篇原创文章 · 获赞 0 · 访问量 121

猜你喜欢

转载自blog.csdn.net/zq6269/article/details/105674565