Netty 回调 与 Channel 执行流程

版权声明:如果觉得好的话,不防点个赞,那点你们认为不对或是需要补充,可以留言啊!本人原创,未经允许不得转载!! https://blog.csdn.net/qq_28289405/article/details/83104904

本人的项目是gradle + netty 

1、build.gradle 

plugins {
    id 'java'
}

group 'com.cxm'
version '1.0-SNAPSHOT'

sourceCompatibility = 1.8
targetCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {

    compile group: 'io.netty', name: 'netty-all', version: '4.1.29.Final'

}

请求的执行流程

 请求流程:
 1、首先会启动 Bootstrap 服务器,服务器里面会关联两个事件循环组(boosGroup,workerGroup)
 2、在服务器启动的时候呢,会关联一个相应的处理器childHandler
 3、在childHandler 里面定义的处理器 TestServerInitializer ,回调方法里面定义的若干个自定义的处理器,
    也可以定义Netty本身内置的ChannelHandler,按照顺序往下走,最终会走到我们自己提供的一个处理器TestHttpServerHandler,
    然后返回结果给客户端。
    

2、代码 :TestServer 

package com.cxm.netty.firstexample;

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;

/**
 * @Date: 2018/10/16 15:35
 * @Description: 服务器启动代码
 */
public class TestServer {

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

        //定义两个事件循环组 (两个线程组)
        //NioEventLoopGroup()可以理解为死循环。不断的接受客户端发起的连接,连接进来之后对连接进行处理,紧接着循环继续运行
        // boosGroup --》线程组不断的从客户端那边接受连接,但是不对连接做任何的处理,直接传给worker
        // workerGroup --》线程组接收到boosGroup传过来的连接,真正的完成对连接的处理。例如获取连接的参数,进行实际的业务处理,把结果返回给客户端
        EventLoopGroup boosGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();

       try{
           //ServerBootstrap 服务端启动,他实际上是一个比较方便的轻松的启动服务端的类
           ServerBootstrap serverBootstrap = new ServerBootstrap();
           // childHandler 子处理器 自己编写的处理器,请求到来时,有我们编写的处理器进行处理。
           serverBootstrap.group(boosGroup,workerGroup).channel(NioServerSocketChannel.class).childHandler(new TestServerInitializer());

           ChannelFuture channelFuture = serverBootstrap.bind(8899).sync();
           channelFuture.channel().closeFuture().sync();
       }finally {
           //优雅的关闭
           boosGroup.shutdownGracefully();
           workerGroup.shutdownGracefully();
       }
    }
}

3、TestServerInitializer

package com.cxm.netty.firstexample;

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

/**
 * @Date: 2018/10/16 16:00
 * @Description: 初始化器
 */
public class TestServerInitializer extends ChannelInitializer<SocketChannel> {

    //连接被创建之后会立刻执行 initChannel 回调方法
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        //pipeline 是 根据业务请求完成处理
        ChannelPipeline pipeline = ch.pipeline();

        /**
         * 在最后添加 自定义的若干个处理器
         * HttpServerCodec 是 HttpRequestDecoder 和 HttpResponseEncoder 的组合 ,他可以得到更为简化的http端的实现。
         * 那么这两个是什么意思呢,就是请求从客户端发向给服务器端之后呢,会做一个解码。所谓解码就是把这些
             请求里的信息都提取出来,完成相应的编解码这样的一个动作。
         * ①、HttpRequestDecoder
         * ②、HttpResponseEncoder 是http响应的编码,就是向客户端输送响应的时候,要把这些响应做一个编码的工作。
         * 那么这些功能Netty底层提供了一些基础,帮助更好的完成这个功能。
         * httpServerCodec 相当于把 HttpRequestDecoder 和 HttpResponseEncoder 这两个主键合二为一了,
             通过一个主键就能完成相应的工作。
         *
         */
        pipeline.addLast("httpServerCodec ",new HttpServerCodec());
        //添加自定义的处理器
        pipeline.addLast("testHttpServerHandler",new TestHttpServerHandler());
    }
}

4、TestHttpServerHandler

package com.cxm.netty.firstexample;

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;

/**
 * @Date: 2018/10/16 16:08
 * @Description: 定义自己的处理器
 * Inbound : 对进来请求的处理
 * Outbound : 对返回响应的处理
 */
public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {

    /**
     * Netty本身并不是根据servlet规范进行的,它在处理的时候其实有很多地方都是需要手动处理的;
     * curl 工具是一个完整的网络命令工具,当它请求完之后,紧接着这个请求就会结束,结束程序就会关闭;服务器端就会搜到
            响应的通知。
     * 1、对于浏览器来说,却不是这样的。对于http协议,它是基于请求、响应模式的。无状态的协议。对于Metty来说,当请求来的时候,
            它本身是监听的,监听TCP的端口号;http协议底层是基于socket,TCP这种带连接的协议,他的最底层最底层是serversocket,
            依然是它进行死循环。
     * 2、对于应用来说,当获取客户端的请求之后,首先要判断一下,请求是基于http1.1 还是1.0协议。
     *      ①、如果是1.1 ,那么它会有一个时间keepline.比如说是 3s ,那么3s到了之后,客户端没有再发出新的请求,那么服务器端
                  会把连接给主动的关闭掉。
     *      ②、如果是1.0 ,那就是一个短连接的协议,请求端发送消息过来之后,服务器端就把这个连接给关闭掉。
     * @param ctx
     * @param msg
     * @throws Exception
     */

    //channelRead0 ---》 读取客户端发送过来真正的请求,并且向客户端返回响应
    //相当于 messageReceived
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {

        //输出的是远程连接的IP地址
        System.out.println(msg.getClass());

        //输出端口号,远程连接的IP地址等信息
        System.out.println(ctx.channel().remoteAddress());
        //睡眠8s
        Thread.sleep(8000);

        if (msg instanceof HttpRequest){

            //强制的向下类型转换
            HttpRequest httpRequest = (HttpRequest)msg;

            System.out.println("请求方法名:" + httpRequest.method().name());

            URI uri = new URI(httpRequest.uri());
            if ("/favicon.ico".equals(uri.getPath())){
                System.out.println("请求favicon.ico");
                return;
            }

            //构造响应的字符串
            ByteBuf byteBuffer = Unpooled.copiedBuffer("Hello World" , CharsetUtil.UTF_8);

            //构造响应
            FullHttpResponse response =new DefaultFullHttpResponse(HttpVersion.HTTP_1_1 , HttpResponseStatus.OK , content);

            //设置 Response 头信息
            response.headers().set(HttpHeaderNames.CONTENT_TYPE,"text_plain");
            response.headers().set(HttpHeaderNames.CONTENT_LENGTH,content.readableBytes());

            //返回给客户端的response对象
            ctx.writeAndFlush(response);

            //调用close方法
            ctx.channel().close();
        }
    }

    /**
     * 通道添加
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception{
        System.out.println("handler added");
        super.handlerAdded(ctx);
    }

    /**
     * 通道注册
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws  Exception{
        System.out.println("channel registered");
        super.channelRegistered(ctx);
    }

    /**
     * 通道处于活动状态
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception{
        System.out.println("channel active");
        super.channelActive(ctx);
    }

    /**
     * 通道不活跃状态
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channel Inavtive");
        super.channelInactive(ctx);
    }

    /**
     * 通道取消注册
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channel unregistered");
        super.channelUnregistered(ctx);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_28289405/article/details/83104904