在本章开始之前,建议大家先明白HTTP协议的原理,这位大神的文章非常好,如果有不明白的可以看看。

由于netty天生就是异步事件驱动的架构,因此基于netty开发的HTTP协议也是异步非阻塞的,并且http协议栈无论在性能和可靠性上都表现非常优异,下面我们将进行netty HTTP协议开发之旅。

HTTP理解客户端和服务器端的交互步骤

客户端和服务器端的交互步骤如下:

1、Client向Server发送http请求。
2、Server端对http请求进行解析。
3、Server端向client发送http响应。
4、Client对http响应进行解析。

在这些步骤中会涉及到http请求的编码、解码,http响应的编码、解码。幸运的是,Netty已经为我们提供了这些工具,整个实例的逻辑图如下所示:

其中红色框中的4个类是Netty提供的,它们其实也是一种Handler,其中Encoder继承自ChannelOutboundHandler,Decoder继承自ChannelInboundHandler,它们的作用是:

1、HttpRequestEncoder:对httpRequest进行编码。
2、HttpRequestDecoder:把流数据解析为httpRequest。
3、HttpResponsetEncoder:对httpResponset进行编码。
4、HttpResponseEncoder:把流数据解析为httpResponse。

该实例涉及到的类有5个:HttpServer HttpServerInboundHandler HttpClient HttpClientInboundHandler ByteBufToBytes

场景描述

我们以文件服务器为例学习netty的http服务端入门开发,场景如下:文件服务器使用HTTP协议对外提供服务,当客户端通过浏览器访问文件服务器时,对访问路径进行检查,检查失败时返回403错误,该页无法访问;如果通过校验,以连接的方式打开当前文件目录,每个目录或者文件都是一个超链接,可以递归访问。如果是目录可以继续递归访问它下面的子目录,或者文件,如果是文件且可读,刚可以在浏览器端直接打开,或者通过【另存为】下载该文件。

代码讲解

代码太多了只对核心代码进行讲解,demo中我都做了详细的介绍,大家把代码下载下来仔细的看看。代码就2个文件不多。下面我们来看一下HttpFileServerHandler类

HttpFileServer类讲解
/**
 * @author 作者 YYD
 * @version 创建时间:2016年8月12日 上午11:10:18
 * @function 
 *  文件服务器使用HTTP协议对外提供服务,当客户端通过浏览器访问文件服务器时,对访问路径进行检查,检查失败返回403错误,该页无法访问;
 *  如果校验通过,以链接的方式打开当前文件目录,每个目录或者文件都是个超链接,可以递归访问。
 *      如果是目录,可以继续递归访问它下面的子目录或者文件,如果是文件且可读,则可以在浏览器上直接打开,或者通过【目标另存为】下载该文件
 */
public class HttpFileServer {
    //默认的url路径是:"/src/com/czh/server/"
    private static final String DEFAULT_URL = "/src/com/czh/";

    public void run(final int port,final String url) throws Exception{
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
            .channel(NioServerSocketChannel.class)
            .childHandler(new ChannelInitializer<SocketChannel>() {

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

                    ch.pipeline().addLast("http-decoder", new HttpRequestDecoder());
                    //把多个消息转化成一个消息(FullHttpRequest或者FullHttpResponse),原因是HTTP解码器在每个HTTP消息中会生成多个消息对象。
                    ch.pipeline().addLast("http-aggregator", new HttpObjectAggregator(65536));
                    ch.pipeline().addLast("http-encoder", new HttpResponseEncoder());
                    //支持处理异步发送大数据文件,但不占用过多的内存,防止发生内存泄漏
                    ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
                    //这个是我们自定义的,处理文件服务器逻辑。主要功能还是在这个文件中
                    ch.pipeline().addLast("http-fileServerHandler", new HttpFileServerHandler(url));
                }
            });
            ChannelFuture future  = b.bind("172.16.1.188",port).sync();//这里写你本机的IP地址
            System.out.println("HTTP 文件目录服务器启动,网址是:"+"http://172.16.1.188:"+port+url);
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

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

        int port =  9090;
        String url = DEFAULT_URL;
        new HttpFileServer().run(port, url);
    }
}

讲解:该类的核心代码在initChannel方法中,添加了不少handler处理器。

HttpRequestDecoder:作用是把流数据解析为httpRequest
HttpObjectAggregator:把多个消息转化成一个消息
HttpResponseEncoder:http服务器端对response编码 
ChunkedWriteHandler:持处理异步发送大数据文件,但不占用过多的内存,防止发生内存泄漏

我们的核心功能还是在HttpFileServerHandler类中下面我们来看一下这个类。

HttpFileServerHandler讲解

这个类的代码太多放不开,我在代码中都做的详细的注释,大家看代码吧,这里就不赘述了。

运行结果

当我们输入错误的网址时,来看一下服务器的反映

当我们输入正确的网址的时候来看一下服务器的反映