Netty初学七 Pipeline与ChannelHandler && 构建客户端与服务端的Pipeline

目录

一、Pipeline与ChannelHandler的构成

二、Pipeline的结构

三、构建客户端与服务端的Pipeline


一、Pipeline与ChannelHandler的构成

        1.一个Channel对应一个连接,channel中所有的处理逻辑都在ChannelPipeline的对象里,ChannelPipeline是一个双向链表,它和Channel也是一对一的关系。

        2.ChannelPipeline里的每个节点都是一个ChannelHandlerContext对象,这个对象可以获得和Channel相关的所有上下文信息,其中ChannelHandler是一块独立的逻辑

        3.ChannelHandler分类:

        第一个子接口是ChannelInboundHandler,它是处理读数据的逻辑,例如,在一端读到数据,首先需要解析这段数据,然后对这段数据做一系列的逻辑处理,最终把响应写到对端。在组装响应之前的所有处理逻辑都可以放在这个子接口里

        第二个子接口为ChannelOutboundHandler,它是处理写数据的逻辑,即在一端组装完响应之后把数据写到对端的逻辑

        ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter,它们实现了两大子接口的所有功能,在默认情况下会把读写时间传播到下一个Handler

        4.ChannelInboundHandler的事件传播示例代码:

 serverBootstrap
        .childHandler(new ChannelInitializer<NioSocketChannel>() {
            protected void initChannel(NioSocketChannel ch) {
                ch.Pipeline().addLast(new InBoundHandlerA());
                ch.Pipeline().addLast(new InBoundHandlerB());
                ch.Pipeline().addLast(new InBoundHandlerC());
            }
        });
public class InBoundHandlerA extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throw
 s Exception {
        System.out.println(“InBoundHandlerA: “ + msg);
        super.channelRead(ctx, msg);
    }
}
public class InBoundHandlerB extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throw
 s Exception {
        System.out.println(“InBoundHandlerB: “ + msg);
        super.channelRead(ctx, msg);
    }
}
public class InBoundHandlerC extends ChannelInboundHandlerAdapter {
    @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throw
 s Exception {
System.out.println(“InBoundHandlerC: “ + msg);
super.channelRead(ctx, msg);
}
}

                在ChannelRead()方法里打印当前Handler的信息,调用父类的channelRead()方法会自动调用inboundHandler的channelRead()方法,并且会将当前inboundHandler里处理完毕的对象传递到下一个InboundHandler,上述例子中传递的对象都是同一个msg

        5.ChannelOutboundHandler的事件传播示例代码:

serverBootstrap
        .childHandler(new ChannelInitializer<NioSocketChannel>() {
            protected void initChannel(NioSocketChannel ch) {
                // inbound,处理读数据的逻辑链
                ch.Pipeline().addLast(new InboundHandlerA());
                ch.Pipeline().addLast(new InboundHandlerB());
                ch.Pipeline().addLast(new InboundHandlerC());
                // outbound,处理写数据的逻辑链
                ch.Pipeline().addLast(new OutboundHandlerA());
                ch.Pipeline().addLast(new OutboundHandlerB());
                ch.Pipeline().addLast(new OutboundHandlerC());
            }
        });
public class OutboundHandlerA extends ChannelOutboundHandlerAdapter 
{
    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelProm
 ise promise)
 throws Exception {
        System.out.println(“OutboundHandlerA: “ + msg);
        super.write(ctx, msg, promise);
    }
}
public class OutboundHandlerB extends ChannelOutboundHandlerAdapter 
{
    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelProm
 ise promise)
 throws Exception {
        System.out.println(“OutboundHandlerB: “ + msg);
        super.write(ctx, msg, promise);
    }
}
public class OutboundHandlerC extends ChannelOutboundHandlerAdapter 
{
public void write(ChannelHandlerContext ctx, Object msg, ChannelProm
 ise promise)
 throws Exception {
System.out.println(“OutboundHandlerC: “ + msg);
super.write(ctx, msg, promise);
}
}

                调用父类的write()方法,父类的write()方法会自动调用下一个outboundHandler的write()方法,并且会将outboundHandler里处理完毕的对象传递给下一个outboundHandler

                上述示例代码的执行结果如图

二、Pipeline的结构

        1.无论我们定义的那种类型的Handler,最终它们都以双向链表的方式连接,实际链表的节点是ChannelHandlerContext

        2.Pipeline的执行顺序:

        虽然上图两种类型的Handler在一个双向链表中,但是这两类Handler的分工不同,inboundHandler的事件通常只会传播到下一个inboundHandler,而outboundHandler的事件通常只会传播到下一个outboundHandler,两者相互不干扰

三、构建客户端与服务端的Pipeline

        1.ChaneelInboundHandlerAdapter和ChannelOutboundHandlerAdapter

        第一个适配器主要用于实现ChannelInboundHandler的所有方法,这样我们就无需编写自己的Handler时不需要实现Handler里的每一种方法,而只需要关注我们想要实现的方法。

        示例代码:

 @Override
 public void channelRead(ChannelHandlerContext ctx, Object msg) throws 
Exception {
ctx.fireChannelRead(msg);
}

        上述代码的作用是接收上一个Handler的输出,里面的mag是上一个Handler的输出,其默认情况下的Adapter会通过fireChannelRead()方法直接把上一个Handler的输出结果传递到下一个Handler,Outbound同理

        2.ByteToMessageDecoder:

        一般情况下,无论是在客户端还是服务端,当我们接收到数据后,首先需要做的就是把二进制数据转换成java对象,netty提供了一个父类专门来做这件事情,如下示例代码:

public class PacketDecoder extends ByteToMessageDecoder {
   @Override
   protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) 
{
       out.add(PacketCodeC.INSTANCE.decode(in));
   }
}

        当继承ByteToMessageDecoder这个类之后,只需要实现decode方法,需要向list里面添加解码后的结果对象,可以自动实现结果向下一个Handler传递

        3.SimpleChannelInboundHandler

        一般情况下,通过if else分支进行逻辑处理,当要处理的指令越来越多的时候,代码会显得非常臃肿,我们可以通过给Pipeline添加多个Handler来解决if else的问题。

        Netty抽象出一个SimpleChannelInboundHandler对象,自动实现了类型的判断和对象的传递

        示例代码:

public class LoginRequestHandler extends SimpleChannelInboundHandler
 <LoginRequestPacket>
{
@Override
 protected void channelRead0(ChannelHandlerContext ctx, LoginRequest
 Packet
 loginRequestPacket) {
// 登录逻辑
}
}

        在channelRead0()方法里,不需要通过if逻辑来判断当前对象是否为本Handler可以处理的对象,这一切都已经交给父类来实现

        4.MessageToByteEncoder

        一般我们在给客户端返回响应之前,需要将响应对象编码成ByteBuf

public class LoginRequestHandler extends SimpleChannelInboundHandler
 <LoginRequestPacket
{
@Override
 protected void channelRead0(ChannelHandlerContext ctx, LoginRequest
 Packet
 loginRequestPacket) {
LoginResponsePacket loginResponsePacket = login(loginRequestPacke
 t);
      ByteBuf responseByteBuf = PacketCodeC.INSTANCE.encode(ctx.allo
 c(),
loginResponsePacket);
        ctx.channel().writeAndFlush(responseByteBuf);
    }
}
public class MessageRequestHandler extends
 SimpleChannelInboundHandler<MessageRequestPacket> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, MessageRequ
 estPacket
 messageRequestPacket) {
        MessageResponsePacket messageResponsePacket = receiveMessage
 (messageRequestPacket);
        ByteBuf responseByteBuf = PacketCodeC.INSTANCE.encode(ctx.allo
 c(),
messageRequestPacket);
        ctx.channel().writeAndFlush(responseByteBuf);
    }
}

        Netty提供了一个特殊的ChannelHandler来处理编码逻辑不需要每一次将响应写到对端的时候都调用一次编码逻辑进行编码,也不需要自行创建ByteBuf

public class PacketEncoder extends MessageToByteEncoder<Packet> {
@Override
 protected void encode(ChannelHandlerContext ctx, Packet packet, ByteB
 uf out) {
PacketCodeC.INSTANCE.encode(out, packet);
}
}

        根据上述代码来看,我们需要做的是把java对象的字段写到ByteBuf对象,不需要自行去分配ByteBuf对象,示例代码如下:

public class LoginRequestHandler extends SimpleChannelInboundHandler
 <LoginRequestPacket
{
@Override
 protected void channelRead0(ChannelHandlerContext ctx, LoginRequest
 Packet
 loginRequestPacket) {
ctx.channel().writeAndFlush(login(loginRequestPacket));
}
}
public class MessageRequestHandler extends SimpleChannelInboundHandl
 er
 <MessageResponsePacket> {
@Override
 protected void channelRead0(ChannelHandlerContext ctx, MessageResp
 onsePacket
 messageRequestPacket) {
ctx.channel().writeAndFlush(receiveMessage(messageRequestPacket))
;
}
}

        5.构建客户端与服务端的Pipeline

        客户端&&服务端示例代码:

bootstrap
        .handler(new ChannelInitializer<SocketChannel>() {
            @Override
            public void initChannel(SocketChannel ch) {
                ch.Pipeline().addLast(new PacketDecoder());
                ch.Pipeline().addLast(new LoginResponseHandler());
                ch.Pipeline().addLast(new MessageResponseHandler());
                ch.Pipeline().addLast(new PacketEncoder());
            }
        });
serverBootstrap
               .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    protected void initChannel(NioSocketChannel ch) {
                        ch.Pipeline().addLast(new PacketDecoder());
                        ch.Pipeline().addLast(new LoginRequestHandler());
                        ch.Pipeline().addLast(new MessageRequestHandler());
                        ch.Pipeline().addLast(new PacketEncoder());
                    }
            });

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值