Netty——自定义处理器

概述

根据消息的流向,处理器可以分为入站处理器出站处理器,也存在既是出站处理器又是入站处理器的情况,但是不建议这么做。

  • 出站处理器
    • 顶层为ChannelInboundHandler
  • 入站处理器
    • 顶层为ChannelOutboundHandler

在处理消息数据时的各种编解码器,本质上都是处理器:无论我们向网络中写入的数据是什么类型(int、char、String、二进制等),其都是以字节流的形式进行的。所以,数据由原本的形式转换为字节流的操作称为编码(encode),将数据由字节流转换为其原本的格式称为解码(decode)。在某种程度上可以理解为:编码是一种出站处理器,解码是一种入站处理器,编解码器称为codec

自定义处理器

Netty其实内置了很多的处理器供开发者使用,除此之外我们可以根据需要自定义处理器或者是编解码器。

下面是一个简单的自定义编解码器示例,此对编解码器的功能是将netty接收到的message,由Bytebuf转换为Long(入站),或者是Long转换为Bytebuf(出站)

编码器

package com.leolee.netty.handler;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;

/**
 * @ClassName MyLongToBytebufEncoder
 * @Description: 编码器
 * @Author LeoLee
 * @Date 2021/2/21
 * @Version V1.0
 **/
public class MyLongToBytebufEncoder extends MessageToByteEncoder<Long> {

    @Override
    protected void encode(ChannelHandlerContext ctx, Long msg, ByteBuf out) throws Exception {

        System.out.println("encode invoked");
        System.out.println("msg:" + msg);
        out.writeLong(msg);
    }
}

解码器

package com.leolee.netty.handler;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;

import java.util.List;

/**
 * @ClassName MyByteToLongDecoder
 * @Description: 解码器
 * @Author LeoLee
 * @Date 2021/2/21
 * @Version V1.0
 **/
public class MyByteToLongDecoder extends ByteToMessageDecoder {

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {

        System.out.println("decode invoked");
        System.out.println("readable bytes num:" + in.readableBytes());

        if (in.readableBytes() >= 8) {//long类型是8歌字节
            out.add(in.readLong());
        }
    }
}

客户端

package com.leolee.netty.handler;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;

/**
 * @ClassName MyClient
 * @Description: socket客户端
 * @Author LeoLee
 * @Date 2020/8/23
 * @Version V1.0
 **/
public class MyClient {

    public static void main(String[] args) throws InterruptedException {

        //客户端只需要一个线程组
        EventLoopGroup eventLoopGroup = new NioEventLoopGroup();

        try {
            //声明客户端启动类
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class).handler(new MyClientInitializer());

            ChannelFuture channelFuture = bootstrap.connect("localhost", 8899).sync();
            channelFuture.channel().closeFuture().sync();
        } finally {
            //优雅关闭
            eventLoopGroup.shutdownGracefully();
        }
    }
}
package com.leolee.netty.handler;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;

/**
 * @ClassName MyClientInitializer
 * @Description:
 * @Author LeoLee
 * @Date 2020/8/23
 * @Version V1.0
 **/
public class MyClientInitializer extends ChannelInitializer<SocketChannel> {


    @Override
    protected void initChannel(SocketChannel ch) throws Exception {

        //声明管道
        ChannelPipeline pipeline = ch.pipeline();
        //编解码器
        pipeline.addLast(new MyByteToLongDecoder());
        pipeline.addLast(new MyLongToBytebufEncoder());
        //自定义处理器
        pipeline.addLast("myClientHandler", new MyClientHandler());
    }
}
package com.leolee.netty.handler;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

import java.time.LocalDateTime;

/**
 * @ClassName MyClientHandler
 * @Description:
 * @Author LeoLee
 * @Date 2020/8/23
 * @Version V1.0
 **/
public class MyClientHandler extends SimpleChannelInboundHandler<Long> {


    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Long msg) throws Exception {

        System.out.println(ctx.channel().remoteAddress());
        System.out.println("Client output:" + msg);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {

        cause.printStackTrace();
        ctx.close();
    }

    /**
     * 功能描述: <br> 该回调方法是连接处理活跃状态
     * 〈〉由于demo没有办法模拟请求的发送,所以重写这个方法来模拟客户端的消息发送
     * @Param: [ctx]
     * @Return: void
     * @Author: LeoLee
     * @Date: 2020/8/23 16:55
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(123456L);
    }
}

服务端

package com.leolee.netty.handler;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * @ClassName MySocketServer
 * @Description: socket服务端
 * @Author LeoLee
 * @Date 2020/8/23
 * @Version V1.0
 **/
public class MySocketServer {

    public static void main(String[] args) throws InterruptedException {

        //定义线程组 EventLoopGroup为死循环
        //boss线程组一直在接收客户端发起的请求,但是不对请求做处理,boss会将接收到的请i交给worker线程组来处理
        //实际可以用一个线程组来做客户端的请求接收和处理两件事,但是不推荐
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            //启动类定义
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    //子处理器,自定义处理器,服务端可以使用childHandler或者handler,handlerr对应接收线程组(bossGroup),childHandler对应处理线程组(workerGroup)
                    .childHandler(new MySocketServerInitializer());

            //绑定监听端口
            ChannelFuture channelFuture = serverBootstrap.bind(8899).sync();
            //定义关闭监听
            channelFuture.channel().closeFuture().sync();
        } finally {
            //Netty提供的优雅关闭
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
package com.leolee.netty.handler;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;

/**
 * @ClassName MySocketServerInitializer
 * @Description: 一旦客户端和服务端建立联系之后initChannel就会被调用
 * @Author LeoLee
 * @Date 2020/8/23
 * @Version V1.0
 **/
public class MySocketServerInitializer extends ChannelInitializer<SocketChannel> {


    @Override
    protected void initChannel(SocketChannel ch) throws Exception {

        //声明管道
        ChannelPipeline pipeline = ch.pipeline();
        //编解码器
        pipeline.addLast(new MyByteToLongDecoder());
        pipeline.addLast(new MyLongToBytebufEncoder());
        //自定义处理器
        pipeline.addLast("mySocketServerHandler", new MySocketServerHandler());

    }

}
package com.leolee.netty.handler;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

import java.util.UUID;

/**
 * @ClassName MySocketServerHandler
 * @Description: websocket服务端自定义处理器
 * @Author LeoLee
 * @Date 2020/8/23
 * @Version V1.0
 **/
public class MySocketServerHandler extends SimpleChannelInboundHandler<Long> {


    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Long msg) throws Exception {

        System.out.println(ctx.channel().remoteAddress() + ":" + msg);
        ctx.writeAndFlush(654321L);
    }

    /**
     * 功能描述: <br> 重写异常时的处理回调方法
     * 〈〉在这里遇到异常直接关闭掉链接
     * @Param: [ctx, cause]
     * @Return: void
     * @Author: LeoLee
     * @Date: 2020/8/23 15:04
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

运行结果

首先启动服务端,其次是客户端

服务端输出:

decode invoked
readable bytes num:8
/127.0.0.1:50690:123456
encode invoked
msg:654321

客户端输出:

encode invoked
msg:123456
decode invoked
readable bytes num:8
localhost/127.0.0.1:8899
Client output:654321

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值