第一章:概述
Http协议是一个应用层协议。在Http协议之上又构建出来了WebSocket这种双向通信的协议。可以主动在服务端帮我们去推数据,实际上我们现在做一些双向通信的比较很重要的东西,比如:推送,推送不是前端去定时从服务器访问的,而是服务器主动推送的,这里使用WebSocket就相当方便了。
Http协议是也通信协议,传输过程中也是通过二进制字节进行传输的,到了服务器端会对基于Http协议推送过来的数据进行解析的,Netty是可以支持Http协议的,支持对Http协议进行编解码,这实际上意味着Netty可以作为开发过程中的web服务器,而且比我们常规的Http服务器基于Http1.0版本协议的话只支持短连接,即使在Http1.1版本协议当中支持了长链接,这个长链接也是有他的使用范围的,且有时间限制,这就注定了传统的Web服务器是有问题的。作为了Netty来讲,它支持了Http协议的处理就可以作为Web服务器,再加上他的异步和双向通信,他的效率就会很好。后期在SpringWebFlux当中,作为这种响应式Web开发的方式,他的底层实际上就是基于Netty,它很终要的一个工作就是将Netty作为Web服务器,让Netty帮我们完成Http协议的编解码操作。所以对这个了解了之后,实际上就了解了WebFlux的底层是怎么运行的,WebFlux作为Spring对于Web服务的扩充,实际上就是他字节封装了很多的Handler和编解码器,是对Netty的一个扩充。
如果我们想要浏览器输入Http://localhost:8000/hello想发送一个这样的请求。在Web开发当中,这个请求发送完之后,这个请求会访问到我们的Tomcat,现在呢我们不要用Tomcat了,我们使用Netty接收这个请求并做出相应。Netty想做这个事很简单,只需要解析浏览器给我们发送过来的Http协议的数据即可。Http协议的数据分为请求相关的内容和相应先关的内容。
第二章:Http协议
URL统一资源定位符,互联网当中访问,操作功能资源的方式。通过明确的协议+ip地址+端口号+URI就能找到唯一的一个资源。
http://localhost:8989/springweb2/userController/addUser
这是一个典型的restful风格的URL,可以访问我们的SpringMvc和SpringBoot类型的应用。作为这个URL有这样的几个部分组成URL=http://host:port/uri
http://是通信协议,我们也可以不写,如果我们不写的话浏览器会默认给我们加上。
host是主机,还有我们的端口,后边是我们的uri,http协议类型,目前在web或者移动互联网开发中主要使用的协议就是http或者https,其他的协议pop3、ftp等。
host就是ip地址或者是域名
port就是我们的端口,如果设置为80端口的话,这个是可以不写的。
port端口号。什么是端口号
一:客户端如何能找到这个服务资源呢:
IP地址,通过IP地址就可以找到对应的服务器的物理机,通过IP这个唯一标识就可以找到对应的物理机,这就是IP地址的概念,IP地址就是让我们从浩瀚的互联网当中找到我们的唯一的一个一台物理机。找到服务器之后,但是这个服务器上运行着多个程序,包括Tomcat也包括MySQL,通过端口就可以知道对应的应用程序了,端口被一个进程占用着,端口是一个进程或者程序的唯一标识,这个也是绝对不能冲突的。假设我们在这个服务器上在启动一个Tomcat实例,可以的,但是端口号绝对不能重复,所以端口号是进程在这台计算机上的唯一标识。URI统一资源标识符,一个端口号Tomcat可能运行着多个Web应用,多个web应用之间URI是不能重复的,基于URI找到唯一的资源进行操作访问。
整个URL(统一资源定位符)分为四个部分:协议类型+IP地址+端口号+统一资源标识
二:请求报文
请求就是请求协议,我们习惯上称之为请求报文,它分为三部分:请求行+请求头+请求体(请求数据)
1:请求行
请求行分为:请求方式+URL+协议版本(常用的有HTTP/1.0和HTTP/1.1)
请求方式分为:get_post_delete_put_head_trace_connect_options
URL:统一资源定位符,统一资源定位符属于请求行的一部分。
协议版本:常用的就是上边两个,1.1版本的Http协议支持长链接,一次连上之后他不断,可以在有效的范围之内,可以通过这个连接再次请求,有限不像是WebSocket他可以一直连着,有限的意思是在一定的时间范围内。Http1.1的长链接是默认在一个时间范围内,或者你设置一个时间在这个时间范围内,这个连接就不断。
为什么Http/1.1之后,要支持有限的长链接呢?很简单就是为了通信的效率。Http协议是应用层协议,在传输层还是依赖于Tcp协议进行通信的,而TCP协议创建连接的时候的效率是比较低的,需要经过三次握手四次挥手。我们用完一次之后,直接就断,断完之后如果有用在继续进行连接,这样的效率是很低的,所以这也是长链接存在的意义。这样的好处就是如果咱们频繁交互的话,咱们就是避免了频繁的三次握手四次挥手,坏处就是侵占了服务器端的资源,连接进来之后,占用的是我Socket的资源,占用期间这个Socket资源无法为其他客户端进行服务。
2:请求头
请求头是一些描述的信息。这些描述的信息是我们的客户端主动给我们的服务端发的。这样更有利于服务器了解client相关的情况。
User-Agent:发送请求的应用程序名【浏览器版本,OkHttp,PostMan】
Content-Length:请求发送的数据的大小。
Accept-Encoding:通知服务器可以发送的数据压缩格式gzip
Accept-Language:通知服务器可以发送的语言
Cookie:cookie的数据是通过请求头提交到服务器的
Content-Type:告诉服务器,我现在发送的数据是什么类型的
MIME类型: application/json:json application/xml:xml application/x-www-form-urlencoded:普通表单 text/html:html text/plain:普通文本 image/jpg
我们使用浏览器发送的话,浏览器会给我们设置这些头,如果我们使用程序的话,我们可以在程序当中设置这些头。
3:请求体
请求体就是数据,一定要注意的是只有post提交的方式放到请求体当中,get的方式发送数据他的数据是放到请求行当中URL当中,最标准的表述是Get方法提交的数据是放到请求行当中的URL当中,我们说的这些都是RestFul的体系当中讲的,RestFul体系当中Get这种方法就是用来获取数据的,不是用来提交数据的。标准Get方法是不用来在请求体携带数据的,但是你也能这么干,就是不符合标准呗。
三:响应报文
响应报文分为:状态行+响应头+响应体
1:状态行
状态行当中又包括:协议版本(常用的协议版本http/1.0和Http/2.0)+状态码+文本描述
状态码包括:
200-299的状态码表示成功 300-399的状态码指资源重定向 400-499的状态码指客户端请求出错 500-599的状态码指服务端出错 (HTTP/1.1向协议中引入了信息性状态码,范围为100-199
状态码文本描述信息:
200 OK :客户端请求成功 400 Bad Request:客户端请求有语法错误,不能被服务器所理解 401 Unauthorized:请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用 403 Forbidden:服务器收到请求,但是拒绝提供服务(认证失败) 404:请求资源不存在 500 Internal Server Error:服务器发生不可预期的错误
2:响应头
因为客户端和服务器彼此之间相互不认识,所以需要通过响应头,告知Client一些附加的描述信息,这样可以更加有利于Client更加正确的处理服务器端响应的数据。
Server:服务器软件的名字
Content-Length:服务器响应内容的大小
Content-Type :服务器响应数据的类型,便于浏览器处理内容同上
Set-Cookie :发送Cookie
response.setContentType("text/html;charset=UTF-8"); response.setContentType("application/json");
3:响应体
指的就是服务器相应的内容,如:Html或者JSON。
第三章:Netty对于Http协议编解码
一:核心类型HttpObject
如果我们在整个处理过程中,我们在服务端使用Netty,浏览器发送请求的话,我们Netty的服务端需要按照Http协议的标准来进行解析,整个的解析过程就是一个编解码的过程,解析请求就得分请求行,请求头,请求体,提供响应就得处理状态行,响应头,响应体。这些东西在Netty当中也看成了一种编解码的操作。比如站在我的服务器端,我接收了你客户端浏览器的请求,使用的是Http协议,给到我们的就是一些Http格式的数据,我们就可以根绝这些做解码的工作,把他封装成一些Java对象,便于我们的Netty使用。响应的时候也是将一些Java对象转换为Http格式的数据返回给浏览器。所以在Netty看来,对于Http协议的处理也是分为解码和编码的,这里的解码和编码和我们前期讲解的内容是高度重合的。所谓的解码指的就是把对应的Http协议转换为Java的对象,作为的编码就是把我们的Java的对象转换为Http协议格式的数据二进制内容。整个的这个过程Netty对我们做了很好的封装,这个封装就是我们的HttpObject(Interface),这个就是Netty对于我们Http协议处理过程当中的一个核心的类型。
后续Http浏览器给我们发送数据之后,我们可以把这个数据解析成HttpObject,所以Netty在后期针对于Http开发的过程中提供了HttpObject这些个编解码器,其次会把Http协议的二进制数据和Netty为我们提供的这个HttpObejct对象进行相互的转换。
二:使用Netty开发一个基于Http协议的服务器
HttpServerCodec这个是HttpObject的一个编解码器。在接收请求的时候将Http协议的二进制数据转换为HttpServerCodec,在提供响应的时候我们需要编码了,这个编码就是将HttpObject类型的对象转换为Http协议相应给HttpClient。HttpServerCodec继承了CombineChannelDuplexHandLer(组合),再查看他的泛型,我们就知道换句话说这个类既能编码又能解码。当然我们也可以将这个Handler拆开成两个单独的Handler去处理。
public final class HttpServerCodec extends CombinedChannelDuplexHandler<HttpRequestDecoder, HttpResponseEncoder> implements HttpServerUpgradeHandler.SourceCodec {}
在通过这个解码之后,我们在通过ChannelInboundHandlerAdapter来进行接收获取到解码的数据,这个时候我们看到的MSG就是通过解码器解码之后封装好的HttpObject对象。所以,我们从这个角度来看,对于Http协议的解析也就只是添加了一个Http协议的编解码器,仅此而已。
通过Ctrl+Shift+F10运行服务器,我们在浏览器当中基于Http://localhost:8000来向服务器发送Http协议的数据。
浏览器URL:
http://localhost:8000/hello
服务端代码:
public class NettyHttpServer { private static final Logger log = LoggerFactory.getLogger(NettyHttpServer.class); public static void main(String[] args) { ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.channel(NioServerSocketChannel.class); serverBootstrap.group(new NioEventLoopGroup()); serverBootstrap.childHandler(new ChannelInitializer<NioSocketChannel>() { @Override protected void initChannel(NioSocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new LoggingHandler()); //与http协议相关的编解码器 // 接受请求的时候,解码 http协议转换httpObject // 提供相应的时候,编码 httpObject转换成http协议响应给client pipeline.addLast(new HttpServerCodec()); /* pipeline.addLast(new HttpRequestDecoder()); pipeline.addLast(new HttpResponseEncoder());*/ pipeline.addLast(new ChannelInboundHandlerAdapter() { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { log.debug(" msg is {} ", msg); } }); } }); //client浏览器 http://localhost:8000/xxx serverBootstrap.bind(8000); } }
服务端日志:
2022-11-20 20:08:48.841 [nioEventLoopGroup-2-4] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x6cb4462e, L:/0:0:0:0:0:0:0:1:8000 - R:/0:0:0:0:0:0:0:1:56603] READ COMPLETE 2022-11-20 20:08:48.841 [nioEventLoopGroup-2-4] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x6cb4462e, L:/0:0:0:0:0:0:0:1:8000 ! R:/0:0:0:0:0:0:0:1:56603] INACTIVE 2022-11-20 20:08:48.841 [nioEventLoopGroup-2-4] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x6cb4462e, L:/0:0:0:0:0:0:0:1:8000 ! R:/0:0:0:0:0:0:0:1:56603] UNREGISTERED 2022-11-20 20:08:51.337 [nioEventLoopGroup-2-6] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0xf2a4933a, L:/0:0:0:0:0:0:0:1:8000 - R:/0:0:0:0:0:0:0:1:56632] REGISTERED 2022-11-20 20:08:51.337 [nioEventLoopGroup-2-6] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0xf2a4933a, L:/0:0:0:0:0:0:0:1:8000 - R:/0:0:0:0:0:0:0:1:56632] ACTIVE 2022-11-20 20:08:51.339 [nioEventLoopGroup-2-5] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x52df7aeb, L:/0:0:0:0:0:0:0:1:8000 - R:/0:0:0:0:0:0:0:1:56604] READ: 663B +-------------------------------------------------+ | 0 1 2 3 4 5 6 7 8 9 a b c d e f | +--------+-------------------------------------------------+----------------+ |00000000| 47 45 54 20 2f 68 65 6c 6c 6f 20 48 54 54 50 2f |GET /hello HTTP/| |00000010| 31 2e 31 0d 0a 48 6f 73 74 3a 20 6c 6f 63 61 6c |1.1..Host: local| |00000020| 68 6f 73 74 3a 38 30 30 30 0d 0a 43 6f 6e 6e 65 |host:8000..Conne| |00000030| 63 74 69 6f 6e 3a 20 6b 65 65 70 2d 61 6c 69 76 |ction: keep-aliv| |00000040| 65 0d 0a 73 65 63 2d 63 68 2d 75 61 3a 20 22 47 |e..sec-ch-ua: "G| |00000050| 6f 6f 67 6c 65 20 43 68 72 6f 6d 65 22 3b 76 3d |oogle Chrome";v=| |00000060| 22 31 30 37 22 2c 20 22 43 68 72 6f 6d 69 75 6d |"107", "Chromium| |00000070| 22 3b 76 3d 22 31 30 37 22 2c 20 22 4e 6f 74 3d |";v="107", "Not=| |00000080| 41 3f 42 72 61 6e 64 22 3b 76 3d 22 32 34 22 0d |A?Brand";v="24".| |00000090| 0a 73 65 63 2d 63 68 2d 75 61 2d 6d 6f 62 69 6c |.sec-ch-ua-mobil| |000000a0| 65 3a 20 3f 30 0d 0a 73 65 63 2d 63 68 2d 75 61 |e: ?0..sec-ch-ua| |000000b0| 2d 70 6c 61 74 66 6f 72 6d 3a 20 22 57 69 6e 64 |-platform: "Wind| |000000c0| 6f 77 73 22 0d 0a 55 70 67 72 61 64 65 2d 49 6e |ows"..Upgrade-In| |000000d0| 73 65 63 75 72 65 2d 52 65 71 75 65 73 74 73 3a |secure-Requests:| |000000e0| 20 31 0d 0a 55 73 65 72 2d 41 67 65 6e 74 3a 20 | 1..User-Agent: | |000000f0| 4d 6f 7a 69 6c 6c 61 2f 35 2e 30 20 28 57 69 6e |Mozilla/5.0 (Win| |00000100| 64 6f 77 73 20 4e 54 20 31 30 2e 30 3b 20 57 69 |dows NT 10.0; Wi| |00000110| 6e 36 34 3b 20 78 36 34 29 20 41 70 70 6c 65 57 |n64; x64) AppleW| |00000120| 65 62 4b 69 74 2f 35 33 37 2e 33 36 20 28 4b 48 |ebKit/537.36 (KH| |00000130| 54 4d 4c 2c 20 6c 69 6b 65 20 47 65 63 6b 6f 29 |TML, like Gecko)| |00000140| 20 43 68 72 6f 6d 65 2f 31 30 37 2e 30 2e 30 2e | Chrome/107.0.0.| |00000150| 30 20 53 61 66 61 72 69 2f 35 33 37 2e 33 36 0d |0 Safari/537.36.| |00000160| 0a 41 63 63 65 70 74 3a 20 74 65 78 74 2f 68 74 |.Accept: text/ht| |00000170| 6d 6c 2c 61 70 70 6c 69 63 61 74 69 6f 6e 2f 78 |ml,application/x| |00000180| 68 74 6d 6c 2b 78 6d 6c 2c 61 70 70 6c 69 63 61 |html+xml,applica| |00000190| 74 69 6f 6e 2f 78 6d 6c 3b 71 3d 30 2e 39 2c 69 |tion/xml;q=0.9,i| |000001a0| 6d 61 67 65 2f 61 76 69 66 2c 69 6d 61 67 65 2f |mage/avif,image/| |000001b0| 77 65 62 70 2c 69 6d 61 67 65 2f 61 70 6e 67 2c |webp,image/apng,| |000001c0| 2a 2f 2a 3b 71 3d 30 2e 38 2c 61 70 70 6c 69 63 |*/*;q=0.8,applic| |000001d0| 61 74 69 6f 6e 2f 73 69 67 6e 65 64 2d 65 78 63 |ation/signed-exc| |000001e0| 68 61 6e 67 65 3b 76 3d 62 33 3b 71 3d 30 2e 39 |hange;v=b3;q=0.9| |000001f0| 0d 0a 53 65 63 2d 46 65 74 63 68 2d 53 69 74 65 |..Sec-Fetch-Site| |00000200| 3a 20 6e 6f 6e 65 0d 0a 53 65 63 2d 46 65 74 63 |: none..Sec-Fetc| |00000210| 68 2d 4d 6f 64 65 3a 20 6e 61 76 69 67 61 74 65 |h-Mode: navigate| |00000220| 0d 0a 53 65 63 2d 46 65 74 63 68 2d 55 73 65 72 |..Sec-Fetch-User| |00000230| 3a 20 3f 31 0d 0a 53 65 63 2d 46 65 74 63 68 2d |: ?1..Sec-Fetch-| |00000240| 44 65 73 74 3a 20 64 6f 63 75 6d 65 6e 74 0d 0a |Dest: document..| |00000250| 41 63 63 65 70 74 2d 45 6e 63 6f 64 69 6e 67 3a |Accept-Encoding:| |00000260| 20 67 7a 69 70 2c 20 64 65 66 6c 61 74 65 2c 20 | gzip, deflate, | |00000270| 62 72 0d 0a 41 63 63 65 70 74 2d 4c 61 6e 67 75 |br..Accept-Langu| |00000280| 61 67 65 3a 20 7a 68 2d 43 4e 2c 7a 68 3b 71 3d |age: zh-CN,zh;q=| |00000290| 30 2e 39 0d 0a 0d 0a |0.9.... | +--------+-------------------------------------------------+----------------+ 2022-11-20 20:08:51.340 [nioEventLoopGroup-2-5] DEBUG com.suns.netty09.NettyHttpServer - msg is DefaultHttpRequest(decodeResult: success, version: HTTP/1.1) GET /hello HTTP/1.1 Host: localhost:8000 Connection: keep-alive sec-ch-ua: "Google Chrome";v="107", "Chromium";v="107", "Not=A?Brand";v="24" sec-ch-ua-mobile: ?0 sec-ch-ua-platform: "Windows" Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Sec-Fetch-Site: none Sec-Fetch-Mode: navigate Sec-Fetch-User: ?1 Sec-Fetch-Dest: document Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 2022-11-20 20:08:51.340 [nioEventLoopGroup-2-5] DEBUG com.suns.netty09.NettyHttpServer - msg is EmptyLastHttpContent 2022-11-20 20:08:51.340 [nioEventLoopGroup-2-5] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x52df7aeb, L:/0:0:0:0:0:0:0:1:8000 - R:/0:0:0:0:0:0:0:1:56604] READ COMPLETE
从日志当中我们可以发现,在处理的过程当中实际上,这个MSG输出了两个内容:一个是DefaultHttpRequest,另外一个是EmptyLastHttpContent。我们需要记录这个问题,后续我们会讲解这个问题。MSG对应两个对象,也就是Http请求协议过来之后,Netty会基于HttpObject解析成两个具体的对象,HttpRequest和HttpContent这两个对象。所以在这我们在开发过程当中就需要进行判断了,进行分门别类的对待。
HttpRequest就是请求头了,其中代表了各种请求头(这个请求头对象当中是包含请求头行的)相关的内容。如果这里是这个对象类型的话,我们就可以获取到请求方式,URI,包括他的协议版本,如果想要拿请求头的话就通过headers()方法拿到即可。
HttpContent是请求体 ,我们可以通过content方法拿到具体的请求体内容。在Get这中请求方式当中,请求体是没有的,所以他的类型是:EmptyLastHttpContent,我们这次的重心研究点在请求头。
public class NettyHttpServer { private static final Logger log = LoggerFactory.getLogger(MyNettyServer.class); public static void main(String[] args) { ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.channel(NioServerSocketChannel.class); serverBootstrap.group(new NioEventLoopGroup()); serverBootstrap.childHandler(new ChannelInitializer<NioSocketChannel>() { @Override // protected void initChannel(NioSocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new LoggingHandler()); //与http协议相关的编解码器 // 接受请求的时候,解码 http协议转换httpObject // 提供相应的时候,编码 httpObject转换成http协议响应给client pipeline.addLast(new HttpServerCodec()); /* pipeline.addLast(new HttpRequestDecoder()); pipeline.addLast(new HttpResponseEncoder());*/ pipeline.addLast(new ChannelInboundHandlerAdapter() { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { log.debug(" msg is {} ", msg); if (msg instanceof HttpRequest) { HttpRequest request = (HttpRequest) msg; //请求头 HttpHeaders headers = request.headers(); //请求行 String uri = request.uri(); HttpVersion httpVersion = request.protocolVersion(); HttpMethod method = request.method(); log.debug("uri {} " + uri); //状态行 DefaultFullHttpResponse response = new DefaultFullHttpResponse(httpVersion, HttpResponseStatus.OK); byte[] bytes = "<h1>Hello Suns</h1>".getBytes(); //response.setHeader(Content-Length,10); response.headers().set(CONTENT_LENGTH, bytes.length); response.content().writeBytes(bytes); ctx.writeAndFlush(response); } else if (msg instanceof HttpContent) { //因为get请求,不通过请求体传递数据,所以这块内容是空。 HttpContent content = (HttpContent) msg; //请求体 ByteBuf content1 = content.content(); } } }); } }); //client浏览器 http://localhost:8000/xxx serverBootstrap.bind(8000); } }
我们如何给客户端进行响应呢?我们可以通过默认的DefaultFullHttpResponse。然后我们需要指定我们响应头的协议版本,请求啥版本响应啥版本,设置我们的响应数据,还需要在我们的相应头当中添加我们响应体的长度。
在这里我们必须用这个DefaultFullHttpResponse这样的话我们的响应头和响应体是在一块的。不用这个的话,还得单独设置响应头和响应体。
以上就是处理Http协议数据的核心代码。这些核心代码是很简单的,原先我们在Tomcat开发的时候,我们并没有设置这个Content.length这是因为Tomcat会默认帮我们设置的。最后通过Netty把他给写出去。
最终的效果:
2022-11-20 20:43:29.491 [nioEventLoopGroup-2-3] DEBUG io.netty.buffer.AbstractByteBuf - -Dio.netty.buffer.checkAccessible: true 2022-11-20 20:43:29.491 [nioEventLoopGroup-2-3] DEBUG io.netty.buffer.AbstractByteBuf - -Dio.netty.buffer.checkBounds: true 2022-11-20 20:43:29.492 [nioEventLoopGroup-2-3] DEBUG io.netty.util.ResourceLeakDetectorFactory - Loaded default ResourceLeakDetector: io.netty.util.ResourceLeakDetector@365a9e34 2022-11-20 20:43:29.503 [nioEventLoopGroup-2-2] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x8bd43681, L:/0:0:0:0:0:0:0:1:8000 - R:/0:0:0:0:0:0:0:1:60412] REGISTERED 2022-11-20 20:43:29.503 [nioEventLoopGroup-2-3] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x9bdbcbf0, L:/0:0:0:0:0:0:0:1:8000 - R:/0:0:0:0:0:0:0:1:60413] REGISTERED 2022-11-20 20:43:29.503 [nioEventLoopGroup-2-2] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x8bd43681, L:/0:0:0:0:0:0:0:1:8000 - R:/0:0:0:0:0:0:0:1:60412] ACTIVE 2022-11-20 20:43:29.503 [nioEventLoopGroup-2-3] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x9bdbcbf0, L:/0:0:0:0:0:0:0:1:8000 - R:/0:0:0:0:0:0:0:1:60413] ACTIVE 2022-11-20 20:43:29.507 [nioEventLoopGroup-2-2] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.maxCapacityPerThread: 4096 2022-11-20 20:43:29.507 [nioEventLoopGroup-2-2] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.maxSharedCapacityFactor: 2 2022-11-20 20:43:29.507 [nioEventLoopGroup-2-2] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.linkCapacity: 16 2022-11-20 20:43:29.507 [nioEventLoopGroup-2-2] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.ratio: 8 2022-11-20 20:43:29.512 [nioEventLoopGroup-2-2] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x8bd43681, L:/0:0:0:0:0:0:0:1:8000 - R:/0:0:0:0:0:0:0:1:60412] READ: 663B +-------------------------------------------------+ | 0 1 2 3 4 5 6 7 8 9 a b c d e f | +--------+-------------------------------------------------+----------------+ |00000000| 47 45 54 20 2f 68 65 6c 6c 6f 20 48 54 54 50 2f |GET /hello HTTP/| |00000010| 31 2e 31 0d 0a 48 6f 73 74 3a 20 6c 6f 63 61 6c |1.1..Host: local| |00000020| 68 6f 73 74 3a 38 30 30 30 0d 0a 43 6f 6e 6e 65 |host:8000..Conne| |00000030| 63 74 69 6f 6e 3a 20 6b 65 65 70 2d 61 6c 69 76 |ction: keep-aliv| |00000040| 65 0d 0a 73 65 63 2d 63 68 2d 75 61 3a 20 22 47 |e..sec-ch-ua: "G| |00000050| 6f 6f 67 6c 65 20 43 68 72 6f 6d 65 22 3b 76 3d |oogle Chrome";v=| |00000060| 22 31 30 37 22 2c 20 22 43 68 72 6f 6d 69 75 6d |"107", "Chromium| |00000070| 22 3b 76 3d 22 31 30 37 22 2c 20 22 4e 6f 74 3d |";v="107", "Not=| |00000080| 41 3f 42 72 61 6e 64 22 3b 76 3d 22 32 34 22 0d |A?Brand";v="24".| |00000090| 0a 73 65 63 2d 63 68 2d 75 61 2d 6d 6f 62 69 6c |.sec-ch-ua-mobil| |000000a0| 65 3a 20 3f 30 0d 0a 73 65 63 2d 63 68 2d 75 61 |e: ?0..sec-ch-ua| |000000b0| 2d 70 6c 61 74 66 6f 72 6d 3a 20 22 57 69 6e 64 |-platform: "Wind| |000000c0| 6f 77 73 22 0d 0a 55 70 67 72 61 64 65 2d 49 6e |ows"..Upgrade-In| |000000d0| 73 65 63 75 72 65 2d 52 65 71 75 65 73 74 73 3a |secure-Requests:| |000000e0| 20 31 0d 0a 55 73 65 72 2d 41 67 65 6e 74 3a 20 | 1..User-Agent: | |000000f0| 4d 6f 7a 69 6c 6c 61 2f 35 2e 30 20 28 57 69 6e |Mozilla/5.0 (Win| |00000100| 64 6f 77 73 20 4e 54 20 31 30 2e 30 3b 20 57 69 |dows NT 10.0; Wi| |00000110| 6e 36 34 3b 20 78 36 34 29 20 41 70 70 6c 65 57 |n64; x64) AppleW| |00000120| 65 62 4b 69 74 2f 35 33 37 2e 33 36 20 28 4b 48 |ebKit/537.36 (KH| |00000130| 54 4d 4c 2c 20 6c 69 6b 65 20 47 65 63 6b 6f 29 |TML, like Gecko)| |00000140| 20 43 68 72 6f 6d 65 2f 31 30 37 2e 30 2e 30 2e | Chrome/107.0.0.| |00000150| 30 20 53 61 66 61 72 69 2f 35 33 37 2e 33 36 0d |0 Safari/537.36.| |00000160| 0a 41 63 63 65 70 74 3a 20 74 65 78 74 2f 68 74 |.Accept: text/ht| |00000170| 6d 6c 2c 61 70 70 6c 69 63 61 74 69 6f 6e 2f 78 |ml,application/x| |00000180| 68 74 6d 6c 2b 78 6d 6c 2c 61 70 70 6c 69 63 61 |html+xml,applica| |00000190| 74 69 6f 6e 2f 78 6d 6c 3b 71 3d 30 2e 39 2c 69 |tion/xml;q=0.9,i| |000001a0| 6d 61 67 65 2f 61 76 69 66 2c 69 6d 61 67 65 2f |mage/avif,image/| |000001b0| 77 65 62 70 2c 69 6d 61 67 65 2f 61 70 6e 67 2c |webp,image/apng,| |000001c0| 2a 2f 2a 3b 71 3d 30 2e 38 2c 61 70 70 6c 69 63 |*/*;q=0.8,applic| |000001d0| 61 74 69 6f 6e 2f 73 69 67 6e 65 64 2d 65 78 63 |ation/signed-exc| |000001e0| 68 61 6e 67 65 3b 76 3d 62 33 3b 71 3d 30 2e 39 |hange;v=b3;q=0.9| |000001f0| 0d 0a 53 65 63 2d 46 65 74 63 68 2d 53 69 74 65 |..Sec-Fetch-Site| |00000200| 3a 20 6e 6f 6e 65 0d 0a 53 65 63 2d 46 65 74 63 |: none..Sec-Fetc| |00000210| 68 2d 4d 6f 64 65 3a 20 6e 61 76 69 67 61 74 65 |h-Mode: navigate| |00000220| 0d 0a 53 65 63 2d 46 65 74 63 68 2d 55 73 65 72 |..Sec-Fetch-User| |00000230| 3a 20 3f 31 0d 0a 53 65 63 2d 46 65 74 63 68 2d |: ?1..Sec-Fetch-| |00000240| 44 65 73 74 3a 20 64 6f 63 75 6d 65 6e 74 0d 0a |Dest: document..| |00000250| 41 63 63 65 70 74 2d 45 6e 63 6f 64 69 6e 67 3a |Accept-Encoding:| |00000260| 20 67 7a 69 70 2c 20 64 65 66 6c 61 74 65 2c 20 | gzip, deflate, | |00000270| 62 72 0d 0a 41 63 63 65 70 74 2d 4c 61 6e 67 75 |br..Accept-Langu| |00000280| 61 67 65 3a 20 7a 68 2d 43 4e 2c 7a 68 3b 71 3d |age: zh-CN,zh;q=| |00000290| 30 2e 39 0d 0a 0d 0a |0.9.... | +--------+-------------------------------------------------+----------------+ 2022-11-20 20:43:29.527 [nioEventLoopGroup-2-2] DEBUG com.suns.netty.MyNettyServer - msg is DefaultHttpRequest(decodeResult: success, version: HTTP/1.1) GET /hello HTTP/1.1 Host: localhost:8000 Connection: keep-alive sec-ch-ua: "Google Chrome";v="107", "Chromium";v="107", "Not=A?Brand";v="24" sec-ch-ua-mobile: ?0 sec-ch-ua-platform: "Windows" Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Sec-Fetch-Site: none Sec-Fetch-Mode: navigate Sec-Fetch-User: ?1 Sec-Fetch-Dest: document Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 2022-11-20 20:43:29.527 [nioEventLoopGroup-2-2] DEBUG com.suns.netty.MyNettyServer - uri {} /hello 2022-11-20 20:43:29.531 [nioEventLoopGroup-2-2] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x8bd43681, L:/0:0:0:0:0:0:0:1:8000 - R:/0:0:0:0:0:0:0:1:60412] WRITE: 58B +-------------------------------------------------+ | 0 1 2 3 4 5 6 7 8 9 a b c d e f | +--------+-------------------------------------------------+----------------+ |00000000| 48 54 54 50 2f 31 2e 31 20 32 30 30 20 4f 4b 0d |HTTP/1.1 200 OK.| |00000010| 0a 43 6f 6e 74 65 6e 74 2d 4c 65 6e 67 74 68 3a |.Content-Length:| |00000020| 20 31 39 0d 0a 0d 0a 3c 68 31 3e 48 65 6c 6c 6f | 19....<h1>Hello| |00000030| 20 53 75 6e 73 3c 2f 68 31 3e | Suns</h1> | +--------+-------------------------------------------------+----------------+ 2022-11-20 20:43:29.532 [nioEventLoopGroup-2-2] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x8bd43681, L:/0:0:0:0:0:0:0:1:8000 - R:/0:0:0:0:0:0:0:1:60412] FLUSH 2022-11-20 20:43:29.532 [nioEventLoopGroup-2-2] DEBUG com.suns.netty.MyNettyServer - msg is EmptyLastHttpContent 2022-11-20 20:43:29.533 [nioEventLoopGroup-2-2] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x8bd43681, L:/0:0:0:0:0:0:0:1:8000 - R:/0:0:0:0:0:0:0:1:60412] READ COMPLETE 2022-11-20 20:43:29.564 [nioEventLoopGroup-2-2] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x8bd43681, L:/0:0:0:0:0:0:0:1:8000 - R:/0:0:0:0:0:0:0:1:60412] READ: 589B +-------------------------------------------------+ | 0 1 2 3 4 5 6 7 8 9 a b c d e f | +--------+-------------------------------------------------+----------------+ |00000000| 47 45 54 20 2f 66 61 76 69 63 6f 6e 2e 69 63 6f |GET /favicon.ico| |00000010| 20 48 54 54 50 2f 31 2e 31 0d 0a 48 6f 73 74 3a | HTTP/1.1..Host:| |00000020| 20 6c 6f 63 61 6c 68 6f 73 74 3a 38 30 30 30 0d | localhost:8000.| |00000030| 0a 43 6f 6e 6e 65 63 74 69 6f 6e 3a 20 6b 65 65 |.Connection: kee| |00000040| 70 2d 61 6c 69 76 65 0d 0a 73 65 63 2d 63 68 2d |p-alive..sec-ch-| |00000050| 75 61 3a 20 22 47 6f 6f 67 6c 65 20 43 68 72 6f |ua: "Google Chro| |00000060| 6d 65 22 3b 76 3d 22 31 30 37 22 2c 20 22 43 68 |me";v="107", "Ch| |00000070| 72 6f 6d 69 75 6d 22 3b 76 3d 22 31 30 37 22 2c |romium";v="107",| |00000080| 20 22 4e 6f 74 3d 41 3f 42 72 61 6e 64 22 3b 76 | "Not=A?Brand";v| |00000090| 3d 22 32 34 22 0d 0a 73 65 63 2d 63 68 2d 75 61 |="24"..sec-ch-ua| |000000a0| 2d 6d 6f 62 69 6c 65 3a 20 3f 30 0d 0a 55 73 65 |-mobile: ?0..Use| |000000b0| 72 2d 41 67 65 6e 74 3a 20 4d 6f 7a 69 6c 6c 61 |r-Agent: Mozilla| |000000c0| 2f 35 2e 30 20 28 57 69 6e 64 6f 77 73 20 4e 54 |/5.0 (Windows NT| |000000d0| 20 31 30 2e 30 3b 20 57 69 6e 36 34 3b 20 78 36 | 10.0; Win64; x6| |000000e0| 34 29 20 41 70 70 6c 65 57 65 62 4b 69 74 2f 35 |4) AppleWebKit/5| |000000f0| 33 37 2e 33 36 20 28 4b 48 54 4d 4c 2c 20 6c 69 |37.36 (KHTML, li| |00000100| 6b 65 20 47 65 63 6b 6f 29 20 43 68 72 6f 6d 65 |ke Gecko) Chrome| |00000110| 2f 31 30 37 2e 30 2e 30 2e 30 20 53 61 66 61 72 |/107.0.0.0 Safar| |00000120| 69 2f 35 33 37 2e 33 36 0d 0a 73 65 63 2d 63 68 |i/537.36..sec-ch| |00000130| 2d 75 61 2d 70 6c 61 74 66 6f 72 6d 3a 20 22 57 |-ua-platform: "W| |00000140| 69 6e 64 6f 77 73 22 0d 0a 41 63 63 65 70 74 3a |indows"..Accept:| |00000150| 20 69 6d 61 67 65 2f 61 76 69 66 2c 69 6d 61 67 | image/avif,imag| |00000160| 65 2f 77 65 62 70 2c 69 6d 61 67 65 2f 61 70 6e |e/webp,image/apn| |00000170| 67 2c 69 6d 61 67 65 2f 73 76 67 2b 78 6d 6c 2c |g,image/svg+xml,| |00000180| 69 6d 61 67 65 2f 2a 2c 2a 2f 2a 3b 71 3d 30 2e |image/*,*/*;q=0.| |00000190| 38 0d 0a 53 65 63 2d 46 65 74 63 68 2d 53 69 74 |8..Sec-Fetch-Sit| |000001a0| 65 3a 20 73 61 6d 65 2d 6f 72 69 67 69 6e 0d 0a |e: same-origin..| |000001b0| 53 65 63 2d 46 65 74 63 68 2d 4d 6f 64 65 3a 20 |Sec-Fetch-Mode: | |000001c0| 6e 6f 2d 63 6f 72 73 0d 0a 53 65 63 2d 46 65 74 |no-cors..Sec-Fet| |000001d0| 63 68 2d 44 65 73 74 3a 20 69 6d 61 67 65 0d 0a |ch-Dest: image..| |000001e0| 52 65 66 65 72 65 72 3a 20 68 74 74 70 3a 2f 2f |Referer: http://| |000001f0| 6c 6f 63 61 6c 68 6f 73 74 3a 38 30 30 30 2f 68 |localhost:8000/h| |00000200| 65 6c 6c 6f 0d 0a 41 63 63 65 70 74 2d 45 6e 63 |ello..Accept-Enc| |00000210| 6f 64 69 6e 67 3a 20 67 7a 69 70 2c 20 64 65 66 |oding: gzip, def| |00000220| 6c 61 74 65 2c 20 62 72 0d 0a 41 63 63 65 70 74 |late, br..Accept| |00000230| 2d 4c 61 6e 67 75 61 67 65 3a 20 7a 68 2d 43 4e |-Language: zh-CN| |00000240| 2c 7a 68 3b 71 3d 30 2e 39 0d 0a 0d 0a |,zh;q=0.9.... | +--------+-------------------------------------------------+----------------+ 2022-11-20 20:43:29.564 [nioEventLoopGroup-2-2] DEBUG com.suns.netty.MyNettyServer - msg is DefaultHttpRequest(decodeResult: success, version: HTTP/1.1) GET /favicon.ico HTTP/1.1 Host: localhost:8000 Connection: keep-alive sec-ch-ua: "Google Chrome";v="107", "Chromium";v="107", "Not=A?Brand";v="24" sec-ch-ua-mobile: ?0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 sec-ch-ua-platform: "Windows" Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8 Sec-Fetch-Site: same-origin Sec-Fetch-Mode: no-cors Sec-Fetch-Dest: image Referer: http://localhost:8000/hello Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 2022-11-20 20:43:29.564 [nioEventLoopGroup-2-2] DEBUG com.suns.netty.MyNettyServer - uri {} /favicon.ico 2022-11-20 20:43:29.565 [nioEventLoopGroup-2-2] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x8bd43681, L:/0:0:0:0:0:0:0:1:8000 - R:/0:0:0:0:0:0:0:1:60412] WRITE: 58B +-------------------------------------------------+ | 0 1 2 3 4 5 6 7 8 9 a b c d e f | +--------+-------------------------------------------------+----------------+ |00000000| 48 54 54 50 2f 31 2e 31 20 32 30 30 20 4f 4b 0d |HTTP/1.1 200 OK.| |00000010| 0a 43 6f 6e 74 65 6e 74 2d 4c 65 6e 67 74 68 3a |.Content-Length:| |00000020| 20 31 39 0d 0a 0d 0a 3c 68 31 3e 48 65 6c 6c 6f | 19....<h1>Hello| |00000030| 20 53 75 6e 73 3c 2f 68 31 3e | Suns</h1> | +--------+-------------------------------------------------+----------------+ 2022-11-20 20:43:29.565 [nioEventLoopGroup-2-2] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x8bd43681, L:/0:0:0:0:0:0:0:1:8000 - R:/0:0:0:0:0:0:0:1:60412] FLUSH 2022-11-20 20:43:29.565 [nioEventLoopGroup-2-2] DEBUG com.suns.netty.MyNettyServer - msg is EmptyLastHttpContent 2022-11-20 20:43:29.565 [nioEventLoopGroup-2-2] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x8bd43681, L:/0:0:0:0:0:0:0:1:8000 - R:/0:0:0:0:0:0:0:1:60412] READ COMPLETE 2022-11-20 20:44:34.832 [nioEventLoopGroup-2-3] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x9bdbcbf0, L:/0:0:0:0:0:0:0:1:8000 - R:/0:0:0:0:0:0:0:1:60413] READ COMPLETE 2022-11-20 20:44:34.834 [nioEventLoopGroup-2-3] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x9bdbcbf0, L:/0:0:0:0:0:0:0:1:8000 ! R:/0:0:0:0:0:0:0:1:60413] INACTIVE 2022-11-20 20:44:34.834 [nioEventLoopGroup-2-3] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x9bdbcbf0, L:/0:0:0:0:0:0:0:1:8000 ! R:/0:0:0:0:0:0:0:1:60413] UNREGISTERED
原先的时候,我们如果想要达到这个效果,我们需要怎么办?站在传统的Web开发的角度,首先我们需要一个Tomcat这样的服务器软件,其次我们还需要遵循JavaEE的规范,JavaEE的规范还需要遵循Servlet的规范,不论是SpringMVC还是Struct这些数据的底层都是Servlet规范。我们现在通过Netty是简单还是复杂?如果我们使用Servlet规范来写这段代码是非常简单的,反而咱们用Netty的方式东西会多一点。
那么我们如何使用Servelt来进行实现呢?
引入一个Servlet规范:
<dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> </dependency>
具体代码:
public class TestServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/html;charset=UTF-8"); PrintWriter writer = resp.getWriter(); writer.print("<h1>hello suns</h1>"); writer.flush(); } }
这样的话,我们就可以看到我们的netty支持对Http协议的处理。我们就可以应用HttpObject相关的子类来完成Web编程了。
为什么读取Http协议二进制数据,Netty会帮我们解析到两个对象呢?一个是头信息一个是体对象,相应的一个是体对象,一个是头对象,这实际是Netty对于解码器操作过程中的处理逻辑,在这个Netty处理过程当中,我们客户端发了一个完成的Http协议的数据过来,这个协议数据包括请求头(包含请求行)和请求体,这个完成的Http协议数据都会到达我们的服务器,当然我们需要做解码。解码的话,我们需要把二进制的这个协议转化和为Message,Netty针对于Http协议数据Netty封装了两种类型的Message,最终存在了一个List集合里边,如下文:
protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception;
1:为什么会有两个对象HttpRequst和HttpContent
这个ByteBuf in 对应的就是Http的二进制数据协议过来之后都是以二进制的数据过来,最终都会收到ByteBuf in这个里边,在这个decode里边做这个解码的操作,将解码完之后称之为Message,会将他放到List当中,Http协议的数据会封装成两个对象,然后将这两个对象放到这里边之后,Netty会去遍历这个集合。第一个部分我们称之为请求头,第二个部分我们称之为请求体。对应的是HttpRequest和HttpContent也放到了这个集合里边。这样Netty遍历这个集合的时候,每一个Message都会走这个PipeLine执行一遍这个Handler所以,这个位置会走两次这个位置,所以会在控制台当中打印两个对象的数据,所以我们实际上走了两遍。因为Netty是对每一个Message进行一次完整的流程处理,这样做是很合理的,而数据是一次性过来的,只不过解码的时候被封装成了多个消息。这个在我们封帧的时候,定长的解码器当中也是封装成了多个的消息,每一个Message都会走一遍我们的PipeLine。这个就是为什么decode方法执行的时候,处理结果接收的时候,Message要做成一个集合(List out)的原因,因为可能会被解析成多个消息,最终被解析成几个消息,我们的Handler就会被执行几次。
等我们后续自定义解码器的时候,我们会理解的更加深刻。
2:如何限定我们的Handler只处理其中某一个对象
pipeline.addLast(new SimpleChannelInboundHandler)我们通过控制这个参数的类型就可以达到实现控制符合类型的Message才会进行处理。
SimpleChannelInboundHandler这个Handler他的作用就是限定住我们关注的消息类型,这样限定的好处就是需要的类型的Message采取处理,否则就不去处理。
public class NettyHttpServer1 { private static final Logger log = LoggerFactory.getLogger(MyNettyServer1.class); public static void main(String[] args) { ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.channel(NioServerSocketChannel.class); serverBootstrap.group(new NioEventLoopGroup()); serverBootstrap.childHandler(new ChannelInitializer<NioSocketChannel>() { @Override protected void initChannel(NioSocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new LoggingHandler()); //与http协议相关的编解码器 // 接受请求的时候,解码 http协议转换httpObject // 提供相应的时候,编码 httpObject转换成http协议响应给client pipeline.addLast(new HttpServerCodec()); /* pipeline.addLast(new HttpRequestDecoder()); pipeline.addLast(new HttpResponseEncoder());*/ //SimpleChannelInboundHandler作用 关注的消息类型 pipeline.addLast(new SimpleChannelInboundHandler<HttpRequest>() { @Override protected void channelRead0(ChannelHandlerContext ctx, HttpRequest msg) throws Exception { //HttpRequest request = (HttpRequest) msg; //请求头 HttpHeaders headers = msg.headers(); //请求行 String uri = msg.uri(); HttpVersion httpVersion = msg.protocolVersion(); HttpMethod method = msg.method(); log.debug("uri {} " + uri); //状态行 DefaultFullHttpResponse response = new DefaultFullHttpResponse(httpVersion, HttpResponseStatus.OK); byte[] bytes = "<h1>Hello Suns</h1>".getBytes(); //response.setHeader(Content-Length,10); response.headers().set(CONTENT_LENGTH, bytes.length); response.content().writeBytes(bytes); ctx.writeAndFlush(response); } }); } }); //client浏览器 http://localhost:8000/xxx serverBootstrap.bind(8000); } }
3:为什么SimpleChannelInboundHandler当中重写的是channelRead0()
channelRead底层调用了channelRead0方法,说明channelRead当中多了些功能。做了一些类型转换,我们直接拿了类型来用就行。也就是在外部channelRead方法当中对这个已经转换为HttpRequest类型了,另外一个就是做了释放。
4:另外一个限定我们的Handler只处理其中某一个对象方法
作为HttpServerCodec在进行解码的时候,将一个Http协议解码成了两个Message,分别为HttpRequest和HttpContent,在处理过程中很麻烦。我们可以将上述HttpMessage进行聚合,聚合成一个FullHttpRequest,所谓full的意思就是在一个类型当中同封装了HttpRequest和HttpContent。
pipeline.addLast(new HttpObjectAggregator(1024)):目的就是将HttpRequest和HttpContent聚合在一起。
后端代码:
public class NettyHttpServer2 { private static final Logger log = LoggerFactory.getLogger(MyNettyServer1.class); public static void main(String[] args) { ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.channel(NioServerSocketChannel.class); serverBootstrap.group(new NioEventLoopGroup()); serverBootstrap.childHandler(new ChannelInitializer<NioSocketChannel>() { @Override protected void initChannel(NioSocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new LoggingHandler()); //HttpServerCodec在进行解码操作时,把一个Http协议解码成了2个Message,分别HttpRequest HttpContent //在处理的过程中麻烦 //1 SimpleChannelInboudHandler 进行了类型的限定 //2 对HttpMessage进行聚合 FullHttpRequest pipeline.addLast(new HttpServerCodec()); //消息通过这个聚合器聚合在一起了,这个聚合器就是为了简化开发,用不用都行。 pipeline.addLast(new HttpObjectAggregator(1024)); pipeline.addLast(new ChannelInboundHandlerAdapter(){ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { //FullHttpRequest 在一个类型中同时封装了HttpRequest,HttpContent log.debug("{} ", msg); DefaultFullHttpRequest request = (DefaultFullHttpRequest) msg; request.headers(); request.content(); super.channelRead(ctx, msg); } }); } }); //client浏览器 http://localhost:8000/xxx serverBootstrap.bind(8000); } }
5:Http协议解码器会出现半包粘包问题?
Http协议有Content-Length根据这个数据长度就可以拿到我们的数据长度,所以他不会出现半包和粘包问题。只要解码器是ByteToMessageDecoder的子类,就不会出现版半包和粘包的问题,要么是因为协议的特点规避掉了半包和粘包的问题,要么就是左右继承这个类ByteToMessageDecoder的子类在自己的实现过程中就会实现封帧的解码器,进而解决掉半包和粘包的问题。但是MessageToMessageDecoder会出现这种问题。自己开发的话脑袋里要时刻绷着这个线。