Netty

Netty的介绍

—>Netty是由JBOSS提供的一个java开源框架,现在为Github上的独立项目;
—>Netty是一个异步的,基于事件驱动的网络应用框架,用以快速开发高性能,高可靠的网络IO程序;
—>Netty主要针对在TCP协议下,面向Clients端的高并发应用,或者Peer-to-Peer场景下的大量数据持续传输的应用
—>Netty本质是一个NIO框架,适用于服务器通讯相关的多种应用场景
—>要透彻理解Netty,需要先学习NIO,这样我们才能阅读Netty的源码;
在这里插入图片描述

Netty的应用场景

互联网行业:
—>在分布式系统中,各个节点之间需要远程服务调用,高性能的RPC框架必不可少(远程过程调用),Netty作为异步高性能的通信框架,往往作为基础通信组件被这些RPC框架利用
—>典型的应用有:阿里分布式服务框架,Dubbo的RPC框架使用Dubbo协议进行节点间通信,Dubbo协议默认使用Netty作为基础通信组件,用于实现各进程节点之间的内部通信;


游戏行业:

还有就是我学习Netty最感兴趣的就是因为游戏服务器会使用这个框架


大数据领域:
—>经典的Hadop的高性能通信和序列化组件(AVRO实现数据文件共享)的RPC框架,默认采用Netty进行挂接点通信
—>他的Netty Service 基于Netty框架二次封装实现


IO模型基本说明

—>i/o模型简单的理解,就是用什么样的通道进行数据的发送和接收,很大程度上决定了程序的性能;
java共支持3种网络编程模型i/o模式:BIO/NIO/AIO
java BIO: 同步并阻塞传统阻塞型,原生javaIO),服务器实现模式为一个连接一个线程,及客户端有一个连接请求时,服务器就会启动一个线程来进行处理,如果这个链接不做任何事情会造成不必要的线程开销也就是阻塞;
在这里插入图片描述

—>java NIO:同步非阻塞,服务器实现模式为一个线程处理多个请求(连接),即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有i/o请求就进行处理
在这里插入图片描述

—>java AIO:异步非阻塞,AIOAIO引入异步通道的概念,采用了Proactor模式,简化了程序编写,有效的请求才会进行启动线程,它的特点是先有操作系统完成后才通知服务器启动线程去处理,一般是用于链接数较多且连接时间较长的应用
AIO暂时没有得到广泛的应用 图略

BIO/NIO/AIO适用场景

—>BIO适合用于连接数目较小且固定的架构,这种方式对服务器起源要求比较高,并发局限于应用中,JDK1.4之前的唯一选择,程序容易理解
—>NIO方式是用于连接数目多且连接比较短的架构,比如聊天服务器,弹幕系统,服务器间的通讯等,编程较为复杂,JDK1.4开始支持
—>AIO方式适用于连接数目多且链接时间比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程较为复杂,JDK1.7开始支持

BIO工作流程

—服务端启动一个ServerSocket
—客户端启动Socket对服务器进行通信,默认情况下服务器端需要对每个客户建立一个线程与之通信
—客户端发出请求后,先咨询服务器是否有线程响应,如果没有则会等待,或者被拒绝,(超时,阻塞)
—如果有响应,客户端线程会等待请求结束后,再继续执行

BIO应用实例

1.写好服务端,通过线程池创建线程与之通讯

package com.rlw.netty;

import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class BIOServer {
    public static void main(String[] args) throws Exception {
        //线程池机制
        //思路
        //1. 创建一个线程池
        //2. 如果有客户端连接,就创建一个线程,与之通讯(单独写一个方法)
        ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
        //创建ServerSocket
        ServerSocket serverSocket = new ServerSocket(6666);
        System.out.println("服务器启动了");
        while (true) {
            System.out.println("线程信息 id =" + Thread.currentThread().getId() + " 名字=" + Thread.currentThread().getName());
            //监听,等待客户端连接
            System.out.println("等待连接....");
            //这里可能会发生阻塞现象,一直卡在这里
            final Socket socket = serverSocket.accept();
            System.out.println("连接到一个客户端");
            //就创建一个线程,与之通讯(单独写一个方法)
            newCachedThreadPool.execute(new Runnable() {
                public void run() { //我们重写
                    //可以和客户端通讯
                    handler(socket);
                }
            });
        }
    }
    //编写一个handler方法,和客户端通讯
    public static void handler(Socket socket) {

        try {
            System.out.println("线程信息 id =" + Thread.currentThread().getId() + " 名字=" + Thread.currentThread().getName());
            byte[] bytes = new byte[1024];
            //通过socket 获取输入流
            InputStream inputStream = socket.getInputStream();
            //循环的读取客户端发送的数据
            while (true) {
                System.out.println("线程信息 id =" + Thread.currentThread().getId() + " 名字=" + Thread.currentThread().getName());
                System.out.println("read....");
                //同样read也有可能卡在管道也会阻塞在这里
                int read =  inputStream.read(bytes);
                if(read != -1) {
                    System.out.println(new String(bytes, 0, read
                    )); //输出客户端发送的数据
                } else {
                    break;
                }
            }
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            System.out.println("关闭和client的连接");
            try {
                socket.close();
            }catch (Exception e) {
                e.printStackTrace();
            }

        }
    }
}

2.打开cmd
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

两个线程各自发送消息会发现线程id不同不难发现BIO就是一个服务一个线程


程序运行流程,为啥两个等待连接??????

服务器启动了
线程信息 id =1 名字=main
等待连接…
连接到一个客户端
线程信息 id =1 名字=main
等待连接…
线程信息 id =14 名字=pool-1-thread-1
线程信息 id =14 名字=pool-1-thread-1
read…
123
线程信息 id =14 名字=pool-1-thread-1
read…

在这里插入图片描述

NIO的工作流程以及实例

NIO点我

Netty正片开始

—>官网netty官网点我
—>Netty是一个异步的,基于事件驱动的网络应用框架,用来快速开发高性能的服务器端和客户端
在这里插入图片描述
Netty版本说明
—>netty版本分为netty3.x和netty4.x、netty5.x
—>因为netty5出现了很大的bug,所以被废弃了,目前最为流行的是4.x
—>netty下载地址
在这里插入图片描述

Netty线程模型基本介绍

—>不同的线程模式,对程序的性能有很大的影响;
—>目前存在的线程模型有:
传统阻塞I/O模型
在这里插入图片描述

Reactor模式
1.基于I/O复用模型,多个连接共用一个阻塞对象,应用程序只需要再一个阻塞对象等待,无需阻塞等待所有链接,当某个连接有新的数据可以处理时,操作系统通知应用程序,线程从阻塞状态返回,开始进行业务处理;
2.基于线程池复用线程资源,不用为每个连接创建线程,将连接完成后的业务处理任务分配给线程进行处理,一个线程可以处理多个连接的业务;
Reactor大体图
在这里插入图片描述

在这里插入图片描述
Reactor核心组成
1)Reactor在一个单独的线程中运行,负责监听和分发事件,分发给适当的处理程序来对IO事件作出反应,就像是公司的电话接线员,他监听来自客户的电话并将线路转移给适当的联系人
2)Handlers处理程序执行I/O事件要完成的实际事件(EventHandler)类似于客户想要与之交谈的公司中的实际官员。Reactor通过调度适当的处理程序来响应I/O事件,处理程序执行非阻塞操作

—>根据Reactor的数量和处理资源池线程的数量不同,有三种典型的实现
单Reactor单线程
在整个程序运行期间只有一个线程
1)Select是前面I/O复用模型介绍的标准网络编程API,可以实现应用程序通过一个阻塞对象监听多路连接请求
2)Reactor对象通过Select监控客户端请求事件,收到事件后通过DIspatch进行分发Handler对象处理链接完成后的后续业务处理
3)如果是建立连接请求事件,则由Acceptor通过Accept处理连接请求,然后创建一个Handler对象处理连接完成后的后续业务处理
4)如果不是建立连接事件,则Reactor会分发调用连接对应的Handler来响应
5)Handler会完成Read->业务处理->Send的完整业务流程
优缺点分析
优点:模型简单,没有多线程,进程通信,竞争的问题,全部都在一个线程中完成
缺点:性能问题,只有一个线程,无法完全发挥多核cpu的性能。handler在处理某个连接上业务时,整个进程无法处理其他连接事件,很容易导致性能瓶颈
缺点:可靠性问题,线程意外终止,或者进入死循环,会导致整个系统通信模块不可用,不能接受和处理外部消息,造成节点故障
适用场景:客户端的数量有限,业务处理非常快速,比如Redis在业务处理的时间复杂度为O(1)的情况下
在这里插入图片描述

单Reactor多线程
优缺点分析
1)优点:可以充分的利用多核cpu的处理能力
2)缺点:多线程数据共享和访问比较复杂,reactor处理所有的事件的监听和响应,在单线程运行,高并发场景容易出现性能瓶颈
在这里插入图片描述

主从Reactor多线程
优缺点分析
1)优点:父线程与子线程的数据交互简单职责明确,父线程只需要接收新连接,子线程完成后续的业务处理
2)优点:父线程与子线程的数据交互简单,Reactor主线程只需要把显得连接传给子线程,子线程无需返回数据
3)缺点:编程复杂
在这里插入图片描述

—>Netty线程模式(Netty主要基于主从Reactor多线程模型做了一定的改进,其中主从Reactor多线程模式有多个Reactor)


Reactor模式小结

理解
1)单Reactor单线程:前台接待员和服务员是同一个人,全程为顾客服务;
2)单Reactor多线程,一个前台接待员,多个服务员,接待员只负责接待
3)主从Reactor多线程,多个前台接待员,多个服务生;
Reactor模式优点
1)响应快,不必为单个同步时间所阻塞,虽然Reactor本身依然是同步的
2)可以最大程度的避免复杂的多线程以及同步问题,并且避免了多线程/进程的切换开销
3)扩展性好,可以方便的通过增加Reactor实例个数来充分的利用cpu资源
4)复用性好,Reactor模型本身与具体时间处理逻辑无关,具有很高的复用性

Netty模型

Netty主要基于主从Reactor多线程模型做了一定改进,其中主从Reactor多线程模型有多个Reactor;
netty的简单流程图
在这里插入图片描述
Bossgroup和Workgroup详解
在这里插入图片描述

Netty模型详细版
在这里插入图片描述
图解:
1.Netty抽象出两组线程池BossGroup专门负责接收客户端的连接,WorkerGroup专门负责网络的读写
2.BosstGroup和WorkGroup类型都是NioEventLoopGroup
3.NioEventLoopGroup相当于一个事件循环组,这个组中含有多个事件循环,每一个事件循环是NioEventLoop
4.NioEventLoop表示一个不断循环的执行处理任务的线程,每个NioEventLoop都有一个selector,用于监听绑定在其上的scoket的网络通讯。
5.NioEventLoopGroup可以有多个线程,即可以含有NioEventLoop
6.每个BossNioEventLoop循环执行的步骤有三步
1)轮询accept事件
2)处理accept事件,与client建立连接,生成NioSocketChannel,并将其注册到某个worker NIOEventLoop上的selector
3)处理任务队列的任务,即runAllTasks
7.每个Worker NIOEventLoop循环执行的步骤
1)轮询read/write事件
2)处理i/o事件,即read/write事件,在对应的NioSocketChannel处理
3)处理任务队列的任务,即runAllTasks
8.每个Worker NIOEventLoop处理业务时,会使用pipeline(管道),pipeline中包含了channel,即通过pipeline可以获取到对应通道,管道中维护了很多的处理器

Netty快速入门实例—TCP服务—show code

第一步引入依赖(也可以直接在pox文件中写入依赖)
在这里插入图片描述
在这里插入图片描述

结合上面的图—>Netty模型详细版梳理

服务端

package com.rlw.netty.simple;

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


public class NettyServer {
    public static void main(String[] args) throws Exception {
        //创建BossGroup和WorkGroup
        /**
         * 说明
         * 1.创建两个线程组bossgroup和workgroup
         * 2.bossGroup只是处理连接请求,真正的和客户业务处理,会交给workGroup
         * 3.两个都是无限循环
         */
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workGroup = new NioEventLoopGroup();

        try {
            //创建服务器端的启动对象,配置参数
            ServerBootstrap bootstrap = new ServerBootstrap();

            //使用链式编程来进行设置
            bootstrap.group(bossGroup, workGroup) //设置两个线程组
                    .channel(NioServerSocketChannel.class) //使用NioSocketChannel作为服务器的通道实现
                    .option(ChannelOption.SO_BACKLOG, 128) //使用线程队列得到连接个数
                    .childOption(ChannelOption.SO_KEEPALIVE,true) //设置保持活动的连接状态
                    .childHandler(new ChannelInitializer<SocketChannel>() {//创建一个通道初始化对象
                        //向pipeline设置处理器
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new NettyServerHandler());
                        }
                    });// 给我们的workerGroup的EventLoop对应的管道设置处理器
            System.out.println("服务器  is  ok");

            //绑定一个端口并且同步,生成了一个ChannelFutrue对象
            ChannelFuture cf = bootstrap.bind(6668).sync();

            //对关闭通道进行监听
            cf.channel().closeFuture().sync();
        } finally {
            //优雅的关闭
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }

    }
}

服务端handler

package com.rlw.netty.simple;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;


/**
 * 1.我们自定义一个handler需要继承netty规定好的某个handler适配器
 * 2.这是我们自定义的handler才能成为一个handler
 */
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
    //读取数据实际(这里我们可以读取客户端发送的消息)

    /**
     * @param ctx:上下文对象,含有管道pipeline 和通道channel
     * @param msg:客户端发送的数据,以对象的方式
     * @throws Exception
     */

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//        super.channelRead(ctx, msg);
        System.out.println("server ctx " + ctx);
        //将msg转成bytebuffer
        ByteBuf buf = (ByteBuf) msg;
        System.out.println("客户端发送的消息是:" + buf.toString(CharsetUtil.UTF_8));
        //客户端地址
        System.out.println("客户端地址:" + ctx.channel().remoteAddress());
    }

    /**
     * 数据读取完毕,回消息
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
//        super.channelReadComplete(ctx);
        //writeAndFlushshi是write + flush
        //将数据写入到缓存,并刷新
        //一般来讲,我们对这个发送的数据进行编码
        ctx.writeAndFlush(Unpooled.copiedBuffer("hello,客户端", CharsetUtil.UTF_8));
    }


    /**
     * 处理异常,一般是需要关闭通道
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//        super.exceptionCaught(ctx, cause);
        ctx.close();
    }
}

客户端

package com.rlw.netty.simple;

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

public class NettyClient {
    public static void main(String[] args) throws Exception {

        //客户端需要一个事件循环组
        EventLoopGroup group = new NioEventLoopGroup();

        try {
            //创建一个客户端启动对象
            //注意客户端使用的不是ServerBootStrap而是Bootstrap
            Bootstrap bootstrap = new Bootstrap();

            //设置相关参数
            bootstrap.group(group) //设置线程组
                    .channel(NioSocketChannel.class) //设置客户端通道的实现类
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new NettyClientHandler());//加入自己的处理器
                        }
                    });
            System.out.println("客户端 is ok。。。");

            //启动客户端去连接服务器端
            ChannelFuture channelFuture = bootstrap.connect("127.0.0.1",6668).sync();
            //给关闭通道进行监听
            channelFuture.channel().closeFuture().sync();
        } finally {

            group.shutdownGracefully();

        }

    }
}

客户端handler

package com.rlw.netty.simple;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.internal.ChannelUtils;
import io.netty.util.CharsetUtil;

public class NettyClientHandler extends ChannelInboundHandlerAdapter {
    //当通道就绪时就会触发该方法

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
//        super.channelActive(ctx);
        System.out.println("client ctx "+ctx);
        ctx.writeAndFlush(Unpooled.copiedBuffer("hello,server: 你好!!!!", CharsetUtil.UTF_8));
    }

    //当通道有读取事件时,会触发
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//        super.channelRead(ctx, msg);
        ByteBuf buf = (ByteBuf) msg;
        System.out.println("服务器回复的消息:"+buf.toString(CharsetUtil.UTF_8));
        System.out.println("服务器的地址"+ctx.channel().remoteAddress());
    }

    //当有异常发生 close关闭
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//        super.exceptionCaught(ctx, cause);
        cause.printStackTrace();
        ctx.close();
    }
}

在这里插入图片描述
在这里插入图片描述
bossGroup和workGroup默认含有的线程数
在这里插入图片描述
bossGroup和workGroup默认含有的线程数也可以指定
在这里插入图片描述
每一个NioEventLoopGroup都有一个自己的selector和taskqueue
在这里插入图片描述
在这里插入图片描述
ctx(上下文)channel和pipeline
在这里插入图片描述
channle和pipeline是一个相互对应的关系 相互关联ctx把他们俩都包含进去了,甚至包含了更多信息,像一个保姆


任务队列详解
任务队列中Task有三种典型使用场景
1)用户程序自定义的普通任务
同步阻塞例子—实际生产中是不允许的
在这里插入图片描述
解决办法一
在这里插入图片描述
debug看一下是否放入到队列中了呢
在这里插入图片描述
那放两个Runable呢?第二个会在多少秒后执行,队列中会放几个任务?
在这里插入图片描述
在这里插入图片描述

2)用户自定义定时任务
在这里插入图片描述
3)非当前Reactor线程调用Channel的各种方法
—>例如在推送系统的业务线程,根据用户的标识,找到对应的Channel引用,然后调用Write类方法向该用户推送消息,就会进入到这种场景,最终的Write会提交到任务队列中后被异步阻塞
在这里插入图片描述

Netty模型
1)Netty抽象出两组线程池,BossGroup专门负责接收客户端连接,WorkerGroup专门负责网络读写操作。
2)NioEventLoop表示一个不断循环执行处理任务的线程,每个NioEvnetLoop都有一个selector,用于监听绑定在其上的socket网络通道。
3)NioEventLoop内部采用串行化设计,从消息的读取->解码->处理->编码->发送,始终有IO线程NioEventLoop负责
· NioEventLoopGroup下包含多个NioEventLoop
· 每个NioEventLoop中包含一个Selector,一个taskQueue
· 每个NioEventLoop的Selector上可以注册监听多个NioChannel
· 每个NioChannel只会绑定在唯一的NioEventLoop上
· 每个NioChannel都绑定有一个自己的ChannelPipeline

异步模型
1)异步的概念和同步相对,当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的组件在完成后,通过状态,通知和回调来通知调用者。
2)Netty中的I/O操作是异步的,包括Bind,Write,Connect等操作会简单的返回一个ChannelFuture。
3)调用者并不能立刻获取结果,而是通过Future-Listener机制,用户可以方便的主动获取或者通过通知机制获得IO的操作结果
4)Netty的异步模型是建立在future和callback的之上的,callbcak就是回调。重点说Future,它的核心思想是:假如一个方法fun,计算过程可能是非常耗时的,等待fun返回显然不合适,那么可以在调用fun的时候,立马返回一个Future,后续可以通过Future去监控方法fun的处理过程(即:Future-Listener机制)
Future-Listener机制
1)当Future对象刚刚创建时,处于非完成状态,调用者可以通过返回的ChannelFuture来获取操作执行的状态,注册监听函数来执行完成后的操作
2)常见操作如下:
·通过idDone方法来判断当前操作是否完成;
·通过isSuccess方法来判断已完成的当前操作是否成功
·通过getCause方法来获取已完成的当前操作失败的原因
·通过isCancelled方法来判断已完成的当前操作是否被取消
·通过addListener方法来注册监听器,当操作已完成(isDone方法返回完成),将会通知指定的监听器;如果Future对象已完成,则通知指定的监听器
在这里插入图片描述
这样做的好处是:相比传统的I/O,执行I/O操作后线程会被阻塞住,直到操作完成;异步处理的好处是不会造成线程阻塞,线程在I/O操作期间可以执行别的程序,在高并发情形下会更稳定和更高的吞吐量。

Netty快速入门实例—HTTP服务—show code

Server

package com.rlw.netty.http;

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;

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

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

        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup,workGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ServerInitializer());
            ChannelFuture channelFuture = bootstrap.bind(8888);
            channelFuture.channel().closeFuture().sync();
        }finally {
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }
}

初始化通道

package com.rlw.netty.http;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;

public class ServerInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        //向管道加入处理器

        //得到管道
        ChannelPipeline pipeline = ch.pipeline();

        //加入一个netty 提供的httpServerCodec ->[coder - decoder]
        //加入HttpServerCode
        //1 HttpServerCodec 是netty提供的处理http的编码解码器
        pipeline.addLast("myHttpServerCodec",new HttpServerCodec());
        //2 增加一个自己自定义的处理器handler
        pipeline.addLast("myHttpServerHandler",new HttpServerHandler());
    }
}

业务处理Handler

package com.rlw.netty.http;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;

import java.net.URI;
import java.nio.charset.Charset;

/**
 * 说明:
 * 1 SimpleChannelInboundHandler就是ChannelInboundHandlerAdapter的子类
 * 2 HttpObject表示客户端和服务器端相互通讯的数据被封装成HttpObject类型
 */
public class HttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {
    //读取客户端数据
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
        //判断msg是不是一个httprequest请求
        if (msg instanceof HttpRequest){
            System.out.println("msg类型" + msg.getClass());
            System.out.println("客户端地址"+ctx.channel().remoteAddress());

            HttpRequest httpRequest = (HttpRequest) msg;
            //获取uri
            URI uri = new URI(httpRequest.uri());
            //过滤指定资源
            if ("/favicon.ico".equals(uri.getPath())){
                System.out.println("不让某个资源请求!!");
                return;
            }

            //回复给浏览器信息【http协议】
            ByteBuf content = Unpooled.copiedBuffer("hello,我是服务器", CharsetUtil.UTF_8);
            //构造一个http的响应,即httpresponse
            FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,content);
            response.headers().set(HttpHeaderNames.CONTENT_TYPE,"text/plain");
            response.headers().set(HttpHeaderNames.CONTENT_LENGTH,content.readableBytes());
            //将构建好的response返回
            ctx.writeAndFlush(response);
        }
    }
}

在这里插入图片描述


Netty核心模块组件

Bootstrap和ServerBootstrap
1)Bootstrap意思是引导,一个netty应用通常由一个Bootstrap开始,主要作用是配置整个Netty程序,串联各个组件,Netty中Bootstrap类是客户端程序的启动引导类,ServerBootstrap是服务端启动引导类
2)常见方法
·public ServerBootstrap group(EventLoopGroup parentGroup,EventLoopGroup childGroup),该方法用于服务端,用来设置两个EventLoop
·public B group(EcentLoopGroup group),该方法用于客户端,用来设置一个EventLoopGroup(bossGroup和workGroup)
·public B channel(Class<? extends C> channelClass),该方法用来设置一个服务器端的通道实现
·public < T >B option(ChannelOption< T > option,T value),用来给ServerChannel添加配置
·public < T > ServerBootstrap childOption(ChannelOption< T > channelOption,T value)用来给接收到的通道添加配置
·public ServerBootstrap childHandler(ChannelHandler childHandler),该方法用来设置业务处理类(自定义的handler)于此之外还有一个handler与之并行,区别就是handler对应bossGroup而childHandler对应workerGroup
·public ChannelFuture bind(int inetPort),该方法用于服务器端,用来设置占用的端口号
·public ChannelFuture connect(String inetHost,int inetPort),该方法用于客户端,用来连接服务器


Future、ChannelFuture
1)Netty中所有的IO操作都是异步的,不能立刻得到消息是否被正确处理,但是可以过一会等他执行完成或者直接注册一个监听,具体的实现就是通过Future和ChannelFutures,他们可以注册一个监听,当操作执行成功或失败时监听会自动触发注册的监听事件
2)常见的方法有
·Channel channel(),返回当前正在进行的IO操作的通道
在这里插入图片描述
·ChannelFuture sync(),等待异步操作执行完毕(把当前操作变成异步操作)


Channel
1)Netty网络通信的组件,能够用于执行网络IO操作
2)通过Channel可获得当前网络连接的通道的状态
3)通过Channel可获得网络连接的配置参数(例如接收缓冲区的大小)
4)Channel提供异步的网络IO操作(如建立连接,读写,绑定端口),异步调用意味着任何IO调用都将立即返回,并且不保证在调用结束时所请求的IO操作已完成
5)调用立即返回一个ChannelFuture实例,通过注册监听器到ChannelFuture上,可以IO操作成功,失败或取消时回调通知调用方
6)支持关联IO操作与对应的处理程序
7)不同协议,不同的阻塞类型的连接都有不同的Channel类型与之对应,常用的Channel类型
·NioSocketChannel,异步的客户端TCP Socket连接
·NioServerSocketChannel,异步的服务器端TCP Socket连接
·NioDatagramChannel,异步的UDP连接
·NioSctpChannel,异步的客户端Sctp连接
·NioSctpServerChannel,异步的Sctp服务器端连接,这些通道涵盖了UDP和TCP网络IO以及文件IO


Selector
1)Netty基于Selector对象实现IO多路复用,通过Selector一个线程可以监听多个连接的Channel事件
2)当向一个Selector中注册Channel后,Selector内部的机制就可以自动不断的查询(Select)这些注册的Channel是否有已就绪的IO事件(例如可读,可写,网络连接完成等),这样程序就可以很简单的使用一个线程高效管理多个Channel,Selector可以管理多个channel


ChannelHandler及其实现类
1)ChannelHandler是一个接口,处理IO事件或拦截IO操作,并将其转发到其ChannelPipeline(业务处理链)中的下一个处理程序
2)ChannelHandler本身并没有提供很多方法,因为这个接口有许多的方法需要实现,方便使用期间,可以继承它的子类
3)ChannelHandler及其实现类一览图
在这里插入图片描述
·ChannelInboundHandler用于处理入站IO事件(比如管道数据向Server溜过来,server在这边读,这就是一个入站)
·ChannelOutboundHandler用于处理出站IO事件(相当于Server往管道里面写)
·ChannelInboundHandlerAdapter用于处理入站IO事件
·ChannelOutboundHandlerAdapter用于处理出站IO操作
`ChannelDuplexHandler用于处理入站和出站事件


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值