简介

之前讲了那么多对于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/

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

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