Netty本身就支持了HTTP协议,内置的HttpServerCodec可以针对HTTP协议进行编解码,开箱即用,非常方便。使用Netty来来实现一个简单的HTTP服务是非常简单的。
代码上传至Gitee:https://gitee.com/panchanghe/netty-project
1. HttpServer
Http服务的启动类,它主要是创建ServerBootstrap引导,让Netty服务可以跑起来。
public class HttpServer {
private final int port;
public HttpServer(int port) {
this.port = port;
}
public static void main(String[] args) {
new HttpServer(9999).start();
}
public void start() {
EventLoopGroup boss = new NioEventLoopGroup(1);
EventLoopGroup worker = new NioEventLoopGroup();
new ServerBootstrap()
.group(boss, worker)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.childHandler(new HttpChannelInitializer())
.localAddress(port)
.bind();
}
}
2. HttpChannelInitializer
ChannelHandler的初始化类,当有新的客户端Channel接入时,它负责对SocketChannel的Pipeline进行初始化。
@ChannelHandler.Sharable
public class HttpChannelInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel sc) throws Exception {
// HTTP协议编解码器
sc.pipeline().addLast(new HttpServerCodec());
// HTTP聚合器,得到一个完整的HTTP请求和响应
sc.pipeline().addLast(new HttpObjectAggregator(1024 * 1024 * 10));
// 文件分块传输
sc.pipeline().addLast(new ChunkedWriteHandler());
// HTTP请求分发器
sc.pipeline().addLast(RequestDispatcher.INSTANCE);
}
}
3. RequestDispatcher
请求分发器,这里实现的比较简单,只针对文件做简单的响应,如果文件不存在,返回404页面。
@ChannelHandler.Sharable
public class RequestDispatcher extends SimpleChannelInboundHandler<FullHttpRequest> {
public static RequestDispatcher INSTANCE = new RequestDispatcher();
private static File BASE_FILE;
static {
BASE_FILE = new File(RequestDispatcher.class.getResource("/static").getFile());
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
String uri = request.uri();
if (uri.startsWith("/static")) {
// 文件传输
File diskFile = new File(BASE_FILE, uri.replace("/static/", ""));
if (diskFile.exists()) {
writeFile(diskFile, ctx, request);
}else {
// 404
write404(ctx, request);
}
}
}
// 404页
private static void write404(ChannelHandlerContext ctx, FullHttpRequest request) {
HttpResponse response = new DefaultHttpResponse(request.protocolVersion(), HttpResponseStatus.NOT_FOUND);
ByteBuf buf = ctx.alloc().directBuffer().writeBytes("<h1>404</h1>".getBytes());
response.headers().add(HttpHeaderNames.CONTENT_TYPE, "text/html");
response.headers().add(HttpHeaderNames.CONTENT_LENGTH, buf.readableBytes());
ctx.write(response);
ctx.write(buf);
ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
}
// 响应文件
private static void writeFile(File diskFile, ChannelHandlerContext ctx, FullHttpRequest request) {
try {
// 文件传输
HttpResponse response = new DefaultHttpResponse(request.protocolVersion(), HttpResponseStatus.OK);
RandomAccessFile file = new RandomAccessFile(diskFile, "r");
FileRegion fileRegion = new DefaultFileRegion(file.getChannel(), 0, file.length());
response.headers().add(HttpHeaderNames.CONTENT_LENGTH, file.length());
ctx.write(response);
ctx.write(fileRegion);
ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
}catch (Exception e){
e.printStackTrace();
}
}
}
4. 测试
我在网上找了一个网页模板,包含Html、Css、Js,将它们放到项目的static目录下,就可以被访问到了。
访问:http://127.0.0.1:9999/static/index.html即可看到效果了。