在上一篇文章,我们用Netty进行了一个建议的HttpServer的编写,在本文,我们对该server的执行流程做简要的分析。
我们在上一节编写的TestHttpServerHandler类是继承SimpleChannelInboundHandler类,该类是专门对客户端发送请求进行处理的类。通过观察源码,我们发现该类又继承了ChannelInboundHandlerAdapter类。
ChannelInboundHandlerAdapter类中有很多回调函数,例如handlerAdded,channelActive,channelInactive,channelRegistered,channelUnregistered等函数。这些函数分别是在handler被加入时,channel被注册和取消注册时以及channel被激活时由Netty框架进行调用。
我们在TestServerInitializer类中覆盖这五个方法,如下所示。
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channel active");
super.channelActive(ctx);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channel inactive");
super.channelInactive(ctx);
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("channel registered");
super.channelRegistered(ctx);
}
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("channel unregistered");
super.channelUnregistered(ctx);
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
System.out.println("handler added");
super.handlerAdded(ctx);
}
然后我们用curl命令对服务器进行请求看看服务器终端中会出现什么结果:
服务器返回结果如下所示:
可以看到server的执行流程是按照如下五步进行的。
但是如果我们用浏览器(Chrome)对服务器进行请求时,却发现命令行返回结果如下图所示。
在该图中,每一步都执行了两次,并且没有channel inactive和channel unregistered输出,这是为什么呢?
打开google开发者工具,点击Network,我们可以看到,浏览器不仅请求了localhost根目录,还请求了favicon.ico文件,所以一共执行了两个请求,所以每一步都执行了两次。
(注意不同浏览器请求不同,比如说Firefox中就不会请求localhost两次)
当我们关闭Chrome标签时,终端输出了如下信息。
所以我们知道在浏览器打开localhost的时候请求一直都保持连接,所以不会输出channel inactive和channel unregistered。
而因为curl是网络命令工具,在curl请求完之后,请求就关闭掉了,所以会直接输出channel inactive和channel unregistered信息。
如果是spring MVC框架,在处理完请求返回给用户之后,Tomcat会保证响应的连接会自动关闭掉。但是Netty需要自己做,需要自己写代码令服务器端主动关闭连接。
在channelRead0函数末尾加上*ctx.channel().close();*代码之后,浏览器在请求过后,服务器会自动关闭连接。
最终代码如下图所示。
package com.first.netty.example;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
import java.net.URI;
import java.nio.ByteBuffer;
public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
if (msg instanceof HttpRequest) {
ByteBuf content = Unpooled.copiedBuffer("Hello, World", CharsetUtil.UTF_8);
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());
ctx.writeAndFlush(response);
ctx.channel().close();
}
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channel active");
super.channelActive(ctx);
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("channel registered");
super.channelRegistered(ctx);
}
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("channel unregistered");
super.channelUnregistered(ctx);
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
System.out.println("handler added");
super.handlerAdded(ctx);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channel inactive");
super.channelInactive(ctx);
}
}