文章目录
- 一、 HTTP 服务器请求过滤功能
- 1 . HTTP 服务器资源请求过滤
- 2 . HTTP 请求过滤方法
- 二、 HTTP 服务器 ( 资源过滤 ) 代码实现
- 1 . 服务器主程序
- 2 . 服务器业务逻辑处理类
- 3 . 执行结果
一、 HTTP 服务器请求过滤功能
1 . HTTP 服务器资源请求过滤
在上述代码案例中 , 运行后 , 服务器端收到了两次 HTTP 请求 , 这里的两次请求 , 一个是请求 http://127.0.0.1:8888/ 地址资源 , 令一个是请求网站的图标 ;
显然我们只关心请求的 http://127.0.0.1:8888/ 地址资源 , 不关心网站图标 ;
服务器端需要屏蔽第二次对图标资源的请求 ;
2 . HTTP 请求过滤方法
过滤 HTTP 请求 , 首先要获取到 HTTP 请求的资源类型 , 下面是获取流程 ;
- 获取 HTTP 请求 : HTTP 请求就是 HttpRequest 对象 , 该请求就是 HttpObject msg 参数 , HttpRequest httpRequest = (HttpRequest) msg ;
- 获取请求资源的 URI 地址 : 通过 HTTP 请求可以获取 URI 资源地址 , URI uri = new URI(httpRequest.uri()) ;
- 屏蔽请求 : 判定 URI 地址路径 , 判定 URI 中的路径中是否包含 ico , uri.getPath().contains(“ico”) , 如果包含 , 那么直接返回 , 不响应本次请求 ;
上述操作都是在 void channelRead0(ChannelHandlerContext ctx, HttpObject msg) 回调方法中执行的 ;
二、 HTTP 服务器 ( 资源过滤 ) 代码实现
1 . 服务器主程序
package kim.hsl.http;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
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.HttpServerCodec;
import kim.hsl.netty.ServerHandler;
/**
-
HTTP 服务器
客户端使用浏览器访问即可
*/
public class HTTPServer {
public static void main(String[] args) {
// 1. 创建 BossGroup 线程池 和 WorkerGroup 线程池, 其中维护 NioEventLoop 线程
// NioEventLoop 线程中执行无限循环操作// BossGroup 线程池 : 负责客户端的连接 // 指定线程个数 : 客户端个数很少, 不用很多线程维护, 这里指定线程池中线程个数为 1 EventLoopGroup bossGroup = new NioEventLoopGroup(1); // WorkerGroup 线程池 : 负责客户端连接的数据读写 EventLoopGroup workerGroup = new NioEventLoopGroup(); // 2. 服务器启动对象, 需要为该对象配置各种参数 ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup, workerGroup) // 设置 主从 线程组 , 分别对应 主 Reactor 和 从 Reactor .channel(NioServerSocketChannel.class) // 设置 NIO 网络套接字通道类型 .option(ChannelOption.SO_BACKLOG, 128) // 设置线程队列维护的连接个数 .childOption(ChannelOption.SO_KEEPALIVE, true) // 设置连接状态行为, 保持连接状态 .childHandler( // 为 WorkerGroup 线程池对应的 NioEventLoop 设置对应的事件 处理器 Handler new ChannelInitializer<SocketChannel>() {// 创建通道初始化对象 @Override protected void initChannel(SocketChannel ch) throws Exception { // 该方法在服务器与客户端连接建立成功后会回调 // 获取管道 ChannelPipeline pipeline = ch.pipeline(); // 为管道加入 HTTP 协议的编解码器 HttpServerCodec, // codec 中的 co 是 coder 编码器的意思, dec 是 decoder 解码器的意思 // 第一个字符串是编解码器的名称 pipeline.addLast("HttpServerCodec" , new HttpServerCodec()); // 为管道 Pipeline 设置处理器 Hanedler pipeline.addLast("HTTPServerHandler", new HTTPServerHandler()); } } ); System.out.println("HTTP 服务器准备完毕 ..."); ChannelFuture channelFuture = null; try { // 绑定本地端口, 进行同步操作 , 并返回 ChannelFuture channelFuture = bootstrap.bind(8888).sync(); System.out.println("HTTP 服务器开始监听 8888 端口 ..."); // 关闭通道 , 开始监听操作 channelFuture.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally { // 出现异常后, 优雅的关闭 bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); }
}
}
2 . 服务器业务逻辑处理类
package kim.hsl.http;
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;
/**
-
HTTP 服务器处理类
-
SimpleChannelInboundHandler 是 ChannelInboundHandlerAdapter 子类
HttpObject 指的是服务器端与客户端处理数据时的数据类型
*/
public class HTTPServerHandler extends SimpleChannelInboundHandler<HttpObject> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
if(msg instanceof HttpRequest){ //判断该 HttpObject msg 参数是否是 Http 请求
System.out.println(ctx.channel().remoteAddress() + " 客户端请求数据 ... ");// HTTP 请求过滤核心代码 ----------------------------------------------------------- // 判定 HTTP 请求类型, 过滤 HTTP 请求 // 获取 HTTP 请求 HttpRequest httpRequest = (HttpRequest) msg; // 获取网络资源 URI URI uri = new URI(httpRequest.uri()); System.out.println("本次 HTTP 请求资源 " + uri.getPath()); // 判定 uri 中请求的资源, 如果请求的是网站图标, 那么直接屏蔽本次请求 if(uri != null && uri.getPath() != null && uri.getPath().contains("ico")){ System.out.println("请求图标资源 " + uri.getPath() +", 屏蔽本次请求 !"); return; } // HTTP 请求过滤核心代码 ----------------------------------------------------------- // 准备给客户端浏览器发送的数据 ByteBuf byteBuf = Unpooled.copiedBuffer("Hello Client", CharsetUtil.UTF_8); // 设置 HTTP 版本, 和 HTTP 的状态码, 返回内容 DefaultFullHttpResponse defaultFullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, byteBuf); // 设置 HTTP 请求头 // 设置内容类型是文本类型 defaultFullHttpResponse.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain"); // 设置返回内容的长度 defaultFullHttpResponse.headers().set( HttpHeaderNames.CONTENT_LENGTH, byteBuf.readableBytes()); // 写出 HTTP 数据 ctx.writeAndFlush(defaultFullHttpResponse); }
}
}
3 . 执行结果
① 运行服务器 :
② 客户端访问 : 经测试 , 用 IE 浏览器请求一次 , 用 360 浏览器请求两次 , 这里用 360 浏览器测试 ;
③ 服务器端日志 : 第一次是正常请求 , 第二次请求 /favicon.ico 网站图标资源 , 因此这里将本次请求屏蔽 ;