龙空技术网

Netty Http 协议

java任我行 527

前言:

当前兄弟们对“netty接收http请求”都比较讲究,各位老铁们都想要了解一些“netty接收http请求”的相关文章。那么小编也在网络上汇集了一些对于“netty接收http请求””的相关知识,希望我们能喜欢,朋友们一起来学习一下吧!

1. 前言

我们通常使用 Netty 来开发 TCP 协议,一般的应用场景都是客户端和服务端长连接通讯的模式,其实,除了 TCP 协议之外 Netty 还支持其他常见的应用协议,比如:Http、WebSocket 等。我们所熟悉的 Tomcat 在 6.x 之后其实底层就是基于 Netty 去实现的。接下来我们主要讲解如何通过 Netty 开发支持 Http 协议服务端,客户端则是通过浏览器发起请求。

2. 学习目的

其实 Netty 开发 Http 协议在我们的开发当中其实并不常用,其主要的的应用场景是开发类型 Tomcat 这种类型的 Web 容器,有了成熟的 Tomcat、Jboss、WebLogic,不需要我们去重新造一遍轮子,但是为什么还需要去学习它呢?

学习本节主要有两个目的:

有助于以后学习 Tomcat 的原理,Tomcat 的通讯部分是基于 Netty 去实现的;有助于理解整个 Java 体系的通讯架构原理,很多我们平时使用最多、接触最多、熟练使用的技术,但是我们往往不懂得其底层原理是什么,Tomcat 和 Http 就是其中被广泛熟知,但是很少同学有兴趣去了解其原理的。3. 环境搭建

下面,我们将实现一个 Demo,具体需求如下:

使用 Netty 开发一个 Web 服务器,端口是 8080;客户端请求,则不再是使用 Netty 编写的客户端代码了,而是通过浏览器输入地址进行访问。服务端响应,我们的 Web 服务器往浏览器输出信息,并且能够在浏览器上打印相关信息。

环境搭建步骤:

创建一个 Maven 项目;导入 Netty 坐标。

实例:

<dependency>    <groupId>io.netty</groupId>    <artifactId>netty-all</artifactId>    <version>4.1.6.Final</version></dependency>代码块12345
4. 代码实现

Netty 核心原理是对客户端发送过来的数据进行解码,以及给客户端发送数据时需要进行数据的编码。同样的原理,Netty 对于 Http 协议的开发,其实也是针对 Http 格式是数据进行编码和解码而已,并没有很多神奇的地方。当然我们对 Http 格式非常的熟悉,可以自己手工去实现这个复杂的过程,Netty 也考虑到了简化开发的复杂度,因此给我们提供了相应的编解码类。接下来,我们一起感受一下。

4.1. Netty 主启动类

public class TestServer {    public static void main(String[] args) throws Exception {        EventLoopGroup bossGroup = new NioEventLoopGroup(1);        EventLoopGroup workerGroup = new NioEventLoopGroup();        try {            ServerBootstrap serverBootstrap = new ServerBootstrap();            serverBootstrap                    .group(bossGroup, workerGroup)                    .channel(NioServerSocketChannel.class)                    .childHandler(new ChannelInitializer<NioSocketChannel>() {                        protected void initChannel(NioSocketChannel ch) {                            ChannelPipeline pipeline = ch.pipeline();                            //1.Netty提供的针对Http的编解码                            pipeline.addLast(new HttpServerCodec());                            //2.自定义处理Http的业务Handler                            pipeline.addLast(new TestHttpServerHandler());                        }                    });            ChannelFuture channelFuture = serverBootstrap.bind(8080).sync();            channelFuture.channel().closeFuture().sync();        } finally {            bossGroup.shutdownGracefully();            workerGroup.shutdownGracefully();        }    }}

代码说明:

这个是 Netty 的基本模板类,跟我们之前写的并没有什么不同,只是它给我们提了一个特殊的类 HttpServerCodec,从字面上都能猜到它就是针对 Http 服务的编解码器。

4.2. Netty 业务 Handler 类

public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {    @Override    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {        if(msg instanceof HttpRequest) {            //1.打印浏览器的请求地址            System.out.println("客户端地址:" + ctx.channel().remoteAddress());                        //2.给浏览器发送的信息,封装成ByteBuf            ByteBuf content = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);            //3.构造一个http的相应,即 httpresponse            FullHttpResponse response = new DefaultFullHttpResponse(                HttpVersion.HTTP_1_1,                 HttpResponseStatus.OK,                 content);			//4.设置响应头信息-响应格式            response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");            //5.设置响应头信息-响应数据长度            response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());            //6.将构建好 response返回            ctx.writeAndFlush(response);        }    }}

代码说明:

浏览器发送过来的数据,被 Netty 被封装成了 HttpObject 对象,我们需要判断 HttpObject 具体所属类型是不是 HttpRequest;请求信息: 可以打印浏览器的请求信息,比如:请求地址、请求方式、请求体内容、请求头内容等;响应信息: 给浏览器响应,必须构造 HttpResponse 对象,并且可以设置响应头信息、响应体信息。

特殊说明:如果不严格按照 Http 响应格式进行输出,浏览器是无法读取服务端的响应。

4.3 测试

浏览器请求截图:

服务端打印截图:

疑惑:为什么浏览器每次请求,服务端都会打印两次呢?

原因:浏览器每次都发起两次请求,一次是业务请求,一次是浏览器的图标请求,具体如下图所示:

4.4. 静态资源过滤

我们需要把非业务请求,也就是静态资源的请求给过滤掉,避免资源的浪费,具体实现如下所示:

public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {    @Override    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {        if(msg instanceof HttpRequest) {            //1.打印浏览器的请求地址            System.out.println("客户端地址" + ctx.channel().remoteAddress());            //2.强制转换成HttpRequest            HttpRequest httpRequest = (HttpRequest) msg;            //3.获取uri, 过滤指定的资源            URI uri = new URI(httpRequest.uri());            if("/favicon.ico".equals(uri.getPath())) {                System.out.println("请求了 favicon.ico, 不做响应");                return;            }            //4.给浏览器发送的信息,封装成ByteBuf            ByteBuf content = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);            //5.构造一个http的相应,即 httpresponse            FullHttpResponse response = new DefaultFullHttpResponse(                HttpVersion.HTTP_1_1,                 HttpResponseStatus.OK,                 content);			//6.设置响应头信息-响应格式            response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");            //7.设置响应头信息-响应数据长度            response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());            //8.将构建好 response返回            ctx.writeAndFlush(response);        }    }}

代码说明:

需要获取浏览器请求的 uri,并且手工判断 uri 是否等于 /favicon.ico,如果是则不往下处理;同时我们可以判断是否是 js、css、img 等资源文件。

5. 小结

本节主要是了解了 Netty 如何开发一个 Web 服务器,并且和浏览器进行通信,需要注意的地方有几点,具体如下:

格式要求,无论是解码和编码都需要严格按照 Http 协议格式要求,否则给浏览器响应数据时,浏览器不能识别;可以跟进 Http 格式,获取和设置相关信息,比如:请求 IP 地址、请求 uri 地址、请求方式、请求头内容、请求体内容等;响应头、响应体等;静态资源的过滤,一般情况下需要过滤掉,否则消耗服务器资源。

标签: #netty接收http请求