乐趣区

关于netty:netty系列之轻轻松松搭个支持中文的服务器

简介

之前讲了那么多对于 netty 的文章,都是讲 netty 的底层原理和实现,各位小伙伴肯定都在想了,看了这么多篇文章,netty 到底无能啥呢?明天让咱们来应用 netty 简简单单搭一个反对中文的服务器,展现一下 netty 的威力。

netty 的 HTTP 反对

明天咱们搭的服务器是反对 HTTP1.1 的服务器。在 netty 中搭建服务器就像是拼房子,找到适合的工具就能够事倍功半。那么要搭建 HTTP 的房子,netty 提供了什么样的工具呢?

在解说 netty 对 HTTP 的反对之前,咱们先看一下 HTTP 的版本倒退状况。

HTTP 的全称是 Hypertext Transfer Protocol,是在 1989 年 World Wide Web 倒退起来之后呈现的标准协议,用来在 WWW 上传输数据。HTTP/1.1 是 1997 年在原始的 HTTP 协定根底上进行的补充和优化。

到了 2015 年,为了适应疾速发送的 web 利用和古代浏览器的需要,倒退出了新的 HTTP/ 2 协定,次要在手机浏览器、延时解决、图像处理和视频解决方面进行了优化。

基本上所有的古代浏览器都反对 HTTP/ 2 协定了,然而还有很多应用程序应用的是老的 HTTP/1.1 协定。netty 为 HTTP2 和 HTTP1 提供了不同的反对包,对于 HTTP1 的反对包叫做 netty-codec-http, 对 HTTP2 反对的包叫做 netty-codec-http2。

本文会解说 netty 对 HTTP1 的反对,将会在后续的文章中持续 HTTP2 的介绍。

netty-codec-http 提供了对 HTTP 的十分有用的一些封装。

首先是代表 HTTP 中传输对象的类 HttpObject,这个类代表着传输中的所有对象。继承这个类的对象有两个十分重要的对象,别离是 HttpMessage 和 HttpContent。

HttpMessage 可能跟我设想的不太一样,它实际上只蕴含了两局部内容,别离是 HttpVersion 和 HttpHeaders,然而并不蕴含任何内容。

public interface HttpMessage extends HttpObject {HttpVersion protocolVersion();

    HttpMessage setProtocolVersion(HttpVersion version);

    HttpHeaders headers();}

这里 HttpVersion 只反对 HTTP/1.0 和 HTTP/1.1 协定。而 HttpHeaders 就是对 HTTP 申请中头对象的封装。

HttpMessage 的子类是 HttpRequest 和 HttpResponse,所以这两个类自身是不带申请内容的。

而具体申请的内容是在 HttpContent 中,HttpContent 继承自 ByteBufHolder,示意它两头能够带有 ByteBuf 的内容信息。

而 HttpContent 真正的实现类就是 DefaultFullHttpRequest 和 DefaultFullHttpResponse,这两个内蕴含了 HTTP 头和 HTTP 申请响应内容信息。

那么问题来了,为什么要把 HTTP 头和 HTTP 内容离开呢?

这就波及到 HTTP1.1 中音讯传输中的压缩机制了。为了晋升传输的效率,一般来说在传输的的过程中都会对象音讯进行压缩,然而对于 HTTP1.1 来说,头部的内容是没方法压缩的,只能压缩 content 局部,所以须要区别对待。

netty 中应用 HTTP 的原理

咱们晓得 netty 底层是客户端和服务器端构建通道,通过通道来传输 ByteBuf 音讯。那么 netty 是怎么反对 HTTP 申请呢?

当客户端向服务器端发送 HTTP 申请之后,服务器端须要把接管到的数据应用解码器解码成为能够被应用程序应用的各种 HttpObject 对象,从而可能在应用程序中对其解析。

netty 提供了 HttpResponseEncoder 和 HttpRequestDecoder 类,来对 HTTP 的音讯进行编码和解码。

如果不想别离应用两个类来进行编码和解码,netty 还提供了 HttpServerCodec 类来进行编码和解码工作。

这个类蕴含了 HttpRequestDecoder 和 HttpResponseEncoder 两局部的工作,能够同时用来进行编码和解码。

100 (Continue) Status

在 HTTP 中有一个独特的性能叫做,100 (Continue) Status,就是说 client 在不确定 server 端是否会接管申请的时候,能够先发送一个申请头,并在这个头上加一个 ”100-continue” 字段,然而临时还不发送申请 body。直到接管到服务器端的响应之后再发送申请 body。

为了解决这种申请,netty 提供了一个 HttpServerExpectContinueHandler 对象,用来解决 100 Status 的状况。

当然,如果你的客户端没有这种申请,那么能够间接应用 HttpObjectAggregator 来将 HttpMessage 和 HttpContent 和合并成为 FullHttpRequest 或者 FullHttpResponse。

为 netty 搭建 HTTP 服务器

有了下面的工作,咱们就能够应用 netty 搭建 http 服务器了。最要害的一点就是在 HttpRequestServerInitializer 增加对应的 codec 和自定义 handler。

    public void initChannel(SocketChannel ch) {ChannelPipeline p = ch.pipeline();
        p.addLast(new HttpServerCodec());
        p.addLast(new HttpServerExpectContinueHandler());
        p.addLast(new HttpRequestServerHandler());
    }

在自定义的 handler 中,咱们须要实现一个性能,就是当收到客户端的申请时候,须要返回给客户端一段欢送语。

首先将取得的 HttpObject 转换成为 HttpRequest 对象,而后依据申请对象构建一个 DefaultFullHttpResponse 对象,而后设置该 response 对象的头,最初将该对象写到 channel 中。

对应的要害代码如下:

 private static final byte[] CONTENT = "欢送来到 www.flydean.com!".getBytes(StandardCharsets.UTF_8);

    public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) {if (msg instanceof HttpRequest) {HttpRequest req = (HttpRequest) msg;

            boolean keepAlive = HttpUtil.isKeepAlive(req);
            FullHttpResponse response = new DefaultFullHttpResponse(req.protocolVersion(), OK,
                                                                    Unpooled.wrappedBuffer(CONTENT));
            response.headers()
//                    .set(CONTENT_TYPE, TEXT_PLAIN)
                    .set(CONTENT_TYPE, "text/plain;charset=utf-8")
                    .setInt(CONTENT_LENGTH, response.content().readableBytes());

            if (keepAlive) {if (!req.protocolVersion().isKeepAliveDefault()) {
                    // 设置 header connection=keep-alive
                    response.headers().set(CONNECTION, KEEP_ALIVE);
                }
            } else {
                // 如果 keepAlive 是 false,则设置 header connection=close
                response.headers().set(CONNECTION, CLOSE);
            }
            ChannelFuture f = ctx.write(response);
            if (!keepAlive) {f.addListener(ChannelFutureListener.CLOSE);
            }
        }
    }

下面的要害代码中 CONTENT 蕴含了中文字符串,咱们应用 getBytes 将其转换成了 UTF- 8 编码的 byte 数组。那么如果要想客户端可能正确辨认 UTF- 8 编码,须要在 response 的 header 中设置内容类型文件为:”text/plain;charset=utf-8″。

最初,应用上面的代码启动 server:

 // server 配置
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {ServerBootstrap b = new ServerBootstrap();
            b.option(ChannelOption.SO_BACKLOG, 1024);
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class)
             .handler(new LoggingHandler(LogLevel.INFO))
             .childHandler(new HttpRequestServerInitializer());

            Channel ch = b.bind(PORT).sync().channel();
            log.info("请关上你的浏览器,拜访 http://127.0.0.1:8000/");
            ch.closeFuture().sync();
        } finally {bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();}

总结

当初,应用你的浏览器拜访你搭建的服务器地址,你就能够失去 ” 欢送来到 www.flydean.com!”。到此一个简略的 netty 服务器就实现了。

本文的例子能够参考:learn-netty4

本文已收录于 http://www.flydean.com/19-netty-http-client-request/

最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

欢送关注我的公众号:「程序那些事」, 懂技术,更懂你!

退出移动版