在 使用Netty实现Http服务器及客户端 中,我们学习到了如何使用Netty来实现一个简单的Http服务器,这里我们只需要将其进行简单的修改,就可以当做一个静态的网页服务器的,其项目结构如下:
首先是其服务端,学习了Netty之后,发现其实都是非常标准的一套写法,如下:
public final class StaticServer {
public static void main(String[] args) throws Exception {
StaticServer server = new StaticServer();
server.start(8080);
}
private void start(int port) throws Exception {
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup work = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
try {
serverBootstrap.group(boss, work)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 512)
.childHandler(new StaticServerInitializer());
ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
channelFuture.channel().closeFuture().sync();
} finally {
boss.shutdownGracefully();
work.shutdownGracefully();
}
}
}
然后就是添加的相关的ChildHandler,添加了些支持Http请求等,其内容如下:
public class StaticServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//增加对http的支持
pipeline.addLast(new HttpServerCodec());
pipeline.addLast(new HttpObjectAggregator(65536));
pipeline.addLast(new ChunkedWriteHandler());
pipeline.addLast(new StaticServerHandler());
}
}
最后就关键的就是我们对请求静态网页资源的请求进行处理,包括获取资源的路径,文件不存在的处理,文件格式的处理,以及限制其访问方式等等
public class StaticServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
// 状态为1xx的话,继续请求
if (HttpUtil.is100ContinueExpected(request)) {
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE);
ctx.writeAndFlush(response);
}
//处理错误或者无法解析的http请求
if (!request.decoderResult().isSuccess()) {
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST,
Unpooled.copiedBuffer("请求失败", CharsetUtil.UTF_8));
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain;charset=UTF-8");
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
//这里设置了只允许GET请求
if (request.method() != HttpMethod.GET) {
DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.FORBIDDEN);
ByteBuf byteBuf = Unpooled.copiedBuffer("只允许GET请求", CharsetUtil.UTF_8);
response.content().writeBytes(byteBuf);
byteBuf.release();
HttpUtil.setContentLength(response, response.content().readableBytes());
ctx.channel().writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
String uri = request.uri();
String path = this.getClass().getClassLoader().getResource("static").getPath() + uri;
File file = new File(path);
//设置不支持favicon.ico文件
if ("/favicon.ico".equals(uri)) {
return;
}
//文件没有发现设置404
if (!file.exists()) {
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NOT_FOUND,
Unpooled.copiedBuffer("访问路径错误", CharsetUtil.UTF_8));
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain;charset=UTF-8");
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
HttpResponse response = new DefaultHttpResponse(request.protocolVersion(), HttpResponseStatus.OK);
//设置文件格式内容
if (path.endsWith(".html")) {
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html;charset=UTF-8");
} else if (path.endsWith(".js")) {
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/x-javascript");
} else if (path.endsWith(".css")) {
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/css;charset=UTF-8");
}
if (HttpUtil.isKeepAlive(request)) {
response.headers().set(HttpHeaderNames.CONTENT_LENGTH, file.length());
response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
}
ctx.write(response);
RandomAccessFile ra = new RandomAccessFile(file, "r");
if (ctx.pipeline().get(SslHandler.class) == null) {
ctx.write(new DefaultFileRegion(ra.getChannel(), 0, ra.length()));
} else {
ctx.write(new ChunkedNioFile(ra.getChannel()));
}
ChannelFuture channelFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
if (!HttpUtil.isKeepAlive(request)) {
channelFuture.addListener(ChannelFutureListener.CLOSE);
}
ra.close();
}
}
上述我们就根据之前的Http进行简单修改实现的静态网页服务器,其实都是差不多,只不过在获取到访问路径后,我们在项目中获取其对应路径在的相关文件资源,然后将其返回给客户端而已。
按照上述设置的路径,这里我们在resources文件目录下的static文件夹下,放置一个简单的html文件及其相关css样式、图片等,该静态网页就是模仿美团首页写的静态网页
启动该项目,我们直接进行访问其index.html文件,其结果截图如下: