Netty 网络通信框架的基本使用

1.Netty

网络应用通信框架,概念性的东西就不多说了,直接说她能做什么,比如我一个应用需要和其他服务通讯,那么就可以用到这个框架,那我http不行吗也行,但是Netty框架封装了更多的功能,不仅仅是通讯。

下面说一下netty的优点

  1. netty功能强大(具体为什么强大,用了就知道)
  2. 线程安全
  3. 高可用
  4. 消除了一写nio层面bug

本身Netty 可以基于BIO、NIO、AIO,但是最常用的就是NIO模型,并且Netty框架也支持reactor三种线程模型。

2. Netty 主从Reactor的设计

这边的boss就是Reactor主线程做的事情,主要是连接的建立,然后worker 就是IO线程池,主要监听读写IO的工作,然后读取数据之后会给pipeline通过handler处理数据并且返回。

2.1 ChannelPipeline 和 ChannelHandler

这两个就是我们要使用的东西,用图来说明一下这两个东西的关系

2.1.1 channelhandler的分类

他有三种类型handler ,看我干嘛看图!面的head就是duplex 类型的都处理,tail是只处理in的数据,

而我们写的时候看我们情况,想添加哪一种就添加哪一种handler

 2.1.1 netty编码

废话不多说直接上代码

Netty编写服务端

package com.zx.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

public class NettyServer {
    public static void main(String[] args) {
        //建立连接线程池,只负责建立socketchannel
        EventLoopGroup boss = new NioEventLoopGroup(1);
        //从属线程池负责io的线程池
        EventLoopGroup worker = new NioEventLoopGroup();
        //可以理解为启动器
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        //组成一个Reactor
        serverBootstrap.group(boss,worker)
                //这个就是主线程监听的类,nio里面是ServerSocketChannel
                .channel(NioServerSocketChannel.class)
                //给主线程添加日志
                .handler(new LoggingHandler(LogLevel.INFO))
                //这边每一个SocketChannel建立的时候都会相应建立一个pipeline 一一对应的
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        ChannelPipeline pipeline = socketChannel.pipeline();
                        pipeline.addLast(new MyInHandler());
                    }
                });
        try {
            ChannelFuture cf = serverBootstrap.bind(8110).sync();
            cf.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            worker.shutdownGracefully();
            boss.shutdownGracefully();
        }
    }
}

客户端的handler 写法,当然这边只重写了部分方法。还有方法自己研究。

package com.zx.netty;

import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

public class MyInHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("通道建立完毕!");
        super.channelActive(ctx);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("读到数据里!你要处理了!");
        ByteBuf byte1 = (ByteBuf) msg;
        byte [] a = new byte[byte1.readableBytes()];
        byte1.readBytes(a);
        String msgNew = new String(a, Charset.defaultCharset());
        System.out.println("读取数据"+ msgNew);
        super.channelRead(ctx, msg);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        System.out.println("数据读取完毕了!");
        Channel channel = ctx.channel();
        //这边的方法是创建一个buff 对象
        ByteBuf buffer = channel.alloc().buffer();
        buffer.writeBytes("你好我是老八".getBytes(StandardCharsets.UTF_8));
        channel.writeAndFlush(buffer);
        super.channelReadComplete(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("有错误了!");
        super.exceptionCaught(ctx, cause);
    }
}

客户端写法这边不贴代码直接图片标注出与客户端不一样的地方,handler 和服务端差不多的写法

2.1.2 Outboundhandler 

这个是监听出去的数据的方法。进来的方法有专门的方法监听出去肯定也有

这边注意一下,Inboundhandler往外写了几次数据,我这个out就会触发几次。并不是把前面所有写的数据汇总然后一起出发,注意这边写了多少次就会触发多少次

package com.zx.netty;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;

import java.nio.charset.StandardCharsets;

public class MyOutHandler extends ChannelOutboundHandlerAdapter {
    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        ByteBuf byte1 = (ByteBuf) msg;
        System.out.println("我看看你要发什么JB数据"+byte1.toString(StandardCharsets.UTF_8));
        super.write(ctx, msg, promise);
    }
}

2.1.3 执行顺序

所有的handler 执行都是有顺序的,我们分析一小段代码

ChannelPipeline pipeline = socketChannel.pipeline();
                        pipeline.addLast(new MyOutHandler());
                        pipeline.addLast(new MyInHandler());
                        pipeline.addLast(new MyInHandler2());

注意看这边有个addLast方法,当然还有子方法addFirst 之前说过handler是双向链表结构的,那么看图

要想顺序没问题out一定要靠近数据的出口。结合上图了解一下

2.1.3 事件传递

这边的super 就是执行的向后传递的工作,因为是链表结构,某种意义上前面不给后面传递,那么后面的事件就没有意义。super注释掉,后面的handler的该事件就不会被触发

@Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("通道建立完毕!");
        //所有的super都是往后传递的,如果注释掉,后面的这个事件就不会发生!注意这边是某个事件,而不是所有事件
        super.channelActive(ctx);
    }

2.1.4 往外写数据的两种方式

一个是channel一个是ctx,这两个是有本质的区别的,如果是channel那么会从最后的tail往前面找outbound方法,如果是ctx 只会从当前handler往前找outbound。这也解释了为什么outbound会触发两次的问题。

@Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        System.out.println("数据读取完毕了!");
        Channel channel = ctx.channel();
        ByteBuf buffer = channel.alloc().buffer();
        buffer.writeBytes("你好我是老八".getBytes(StandardCharsets.UTF_8));
        //channel.writeAndFlush(buffer);
        //这两个有区别,主要的区别是从那边开始返回!
        ctx.writeAndFlush(buffer);
        super.channelReadComplete(ctx);
    }

所以在outhandler里面不能用channel,这样消息就会陷入死循环,能理解这个情况,那么这个顺序理解的就非常到位了。

2.1.4 handler 是可以共享的

我们这边每次都new 了handler,意思就是每个pipleine 都会对应一个新的handler,但是也存在多个pipeline 对应一个handler 只需要在该handler上加一个共享注解@ChannelHandler.Sharable

@ChannelHandler.Sharable
public class MyInHandler2 extends ChannelInboundHandlerAdapter {

2.1.5 Bytebuf

这边内容还比较多,后面会开一篇新的文章介绍一下,这边介绍一下重要的指针。

其实就是我们数学里面追击的场景。这边需要注意的是butebuf你读过的数据,再读是读不到的,

上面的handler读取过的数据往后传递的时候后面的handler是读不出数据的。

 2.2 结束语

这篇主要说一下netty的应用,但是netty里面重要的组件后面会各自单独的开一篇文章解释,内容较多,就不在这篇阐述 了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值