本文转载自:https://www.cnblogs.com/luangeng/p/7875710.html
HTTP协议:略
基于Netty的HTTP协议栈可以方便的进行异步非阻塞的HTTP服务器的开发。
当在浏览器中输入一个指向特定网页的URL时,浏览器就会生成一个HTTP请求,浏览器会与服务器建立TCP连接,当TCP可靠连接建立之后,浏览器会将生成的HTTP请求发送到服务器端。这时服务器程序接收到了信息将要去识别这个信息的内容,然后调用相应的服务程序,经过服务程序的分析和处理之后服务器端返回内容给浏览器。当服务器返回了内容给浏览器后,这时浏览器与服务器之间的数据交换完毕,这时TCP可靠连接就会断开。
下面是一个简单的例子,只处理GET请求:
html,css,js,jpg等资源位置如下
HttpServer
package com.luangeng.netty.http; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpRequestDecoder; import io.netty.handler.codec.http.HttpResponseEncoder; import io.netty.handler.stream.ChunkedWriteHandler; /** * Created by LG on 2017/11/21. */ public class HttpServer { public void run(final int port) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { // HTTP请求消息解码器 ch.pipeline().addLast("http-decoder", new HttpRequestDecoder()); /* * HttpObjectAggregator解码器 * 将多个消息转换为单一的FullHttpRequest或FullHttpResponse对象 */ ch.pipeline().addLast("http-aggregator", new HttpObjectAggregator(65536)); //HTTP响应编码器,对HTTP响应进行编码 ch.pipeline().addLast("http-encoder", new HttpResponseEncoder()); //ChunkedWriteHandler的主要作用是支持异步发送大的码流,但不占用过多的内存,防止JAVA内存溢出 ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler()); ch.pipeline().addLast("httpServerHandler", new HttpServerHandler()); } }); ChannelFuture future = b.bind("localhost", port).sync(); System.out.println("HTTP Server startup."); future.channel().closeFuture().sync(); } catch (Exception e) { e.printStackTrace(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } public static void main(String[] args) throws Exception { int port = 8080; new HttpServer().run(port); } }
---
HttpServerHandler
package com.luangeng.netty.http; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.*; import io.netty.util.CharsetUtil; import java.io.File; import java.io.FileInputStream; import java.net.URLDecoder; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE; import static io.netty.handler.codec.http.HttpMethod.GET; import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; /** * Created by LG on 2017/11/21. */ public class HttpServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> { private static final String BASE_DIR = System.getProperty("user.dir") + "/src/main/java/com/luangeng/netty/http/i"; @Override protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception { //400 if (!request.decoderResult().isSuccess()) { sendError(ctx, HttpResponseStatus.BAD_REQUEST); return; } //405 if (request.method() != GET) { sendError(ctx, HttpResponseStatus.METHOD_NOT_ALLOWED); return; } //404 String uri = request.uri(); uri = URLDecoder.decode(uri, "UTF-8"); uri = uri.replace('/', File.separatorChar); File file = new File(BASE_DIR + uri); if (!file.exists() || !file.isFile()) { sendError(ctx, HttpResponseStatus.NOT_FOUND); return; } FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.OK); FileChannel channel = new FileInputStream(file).getChannel(); ByteBuffer buffer = ByteBuffer.allocate(1024); while (channel.read(buffer) != -1) { buffer.flip(); response.content().writeBytes(buffer); buffer.clear(); } channel.close(); if(uri.endsWith(".css")){ response.headers().set(CONTENT_TYPE, "text/css; charset=UTF-8"); }else { response.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8"); } long fileLength = response.content().readableBytes(); HttpUtil.setContentLength(response, fileLength); ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); if (ctx.channel().isActive()) { sendError(ctx, HttpResponseStatus.INTERNAL_SERVER_ERROR); } } private static void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) { FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, status, Unpooled.copiedBuffer("Failure: " + status.toString() + " ", CharsetUtil.UTF_8)); response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8"); ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); } }
---
hello.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Hello</title> <link rel="stylesheet" type="text/css" href="/back.css"/> <script src="/jquery-1.11.3.min.js"></script> </head> <body> <div style="background:#98bf21;height:100px;100px;position:absolute;"> </div> <p> 一幅图像: <img src="/img1.jpg" width="128" height="128"/> </p> <p> 一幅动画图像: <img id="img1" src="/eg_cute.gif" width="50" height="50"/> </p> <ul> <li>咖啡</li> <li>茶</li> <li>牛奶</li> </ul> </body> <script type="text/javascript"> $(document).ready(function() { timedCount(); }); function timedCount() { var div=$("div"); div.animate({height:'300px',opacity:'0.4'},"slow"); div.animate({'300px',opacity:'0.8'},"slow"); div.animate({height:'100px',opacity:'0.4'},"slow"); div.animate({'100px',opacity:'0.8'},"slow"); t=setTimeout("timedCount()",5000) } </script> </html>
---
运行结果如下:
可见js,css,jpg等资源都可正常加载,