美文网首页深入浅出Netty源码剖析程序员netty
Netty4(十):实现 HttpServer 服务器 、Htt

Netty4(十):实现 HttpServer 服务器 、Htt

作者: 聪明的奇瑞 | 来源:发表于2018-03-22 17:10 被阅读76次

认识 Http 请求与响应

  • 在编写代码之前先了解完整的 Http 请求的组成:
    • 首先包含头信息 HTTP Request
    • 然后包含多个数据 HttpContent
    • 结束的标记是 LastHttpContent
FullHttpRequest
  • 完整的 Http 响应的组成:
    • 首先包含头信息 HTTP response
    • 然后包含多个数据 HttpContent
    • 结束的标记是 LastHttpContent
FullHttpResponse
  • 注意:HTTP 请求和响应由许多部分组成,需要提供一个聚合器合并消息到 FullHttpRequest 和 FullHttpResponse

实现 HttpServer 响应请求

目标:当用浏览器请求 localhost:8080 端口时,HttpServer 响应该请求并返回字符串 test

  • 首先编写 Handler,这里有几个要注意的:
    • 该 Handler 继承自 SimpleChannelInboundHandler 用于处理指定 FullHttpRequest 类型的消息
    • 通过 FullHttpResponse 来响应请求
    • 添加响应头 header 的 length 属性,否则浏览器请求之后一直在刷新
    • channel 读取完成之后需要输出缓冲流,否则浏览器请求之后一直在刷新
public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> {

    private AsciiString contentType = HttpHeaderValues.TEXT_PLAIN;

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {
        System.out.println("class:" + msg.getClass().getName());
        DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
                HttpResponseStatus.OK,
                Unpooled.wrappedBuffer("test".getBytes()));
        HttpHeaders heads = response.headers();
        heads.add(HttpHeaderNames.CONTENT_TYPE, contentType + "; charset=UTF-8");
        heads.add(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
        heads.add(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
        ctx.write(response);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        super.channelReadComplete(ctx);
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        if (null != cause) cause.printStackTrace();
        if (null != ctx) ctx.close();
    }
}
  • 编写 ChannelInitializer 用于配置 Http 请求连接的 Channel,这里的几个 Handler 作用如下:
    • HttpRequestDecoder:用于解码 Http Request
    • HttpResponseEncoder:用于编码 Http Response
    • HttpObjectAggregator:消息聚合器,把多个消息转换为一个单一的 FullHttpRequest 或 FullHttpResponse。其构造器参数含义是消息合并的数据大小,如此代表聚合的消息内容长度不超过 512kb
public class HttpInitializer extends ChannelInitializer {
    @Override
    protected void initChannel(Channel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast("decoder", new HttpRequestDecoder())
                .addLast("encoder", new HttpResponseEncoder())
                .addLast("aggregator", new HttpObjectAggregator(512 * 1024))
                .addLast("handler", new HttpHandler());
    }
}
  • 编写服务器
public class HttpServer {
    private final int port;

    public HttpServer(int port) {
        this.port = port;
    }

    public static void main(String[] args) throws Exception {
        int port = (args.length == 1) ? Integer.parseInt(args[0]) : 8080;
        new HttpServer(port).start();
    }

    public void start() throws Exception {
        NioEventLoopGroup group = new NioEventLoopGroup();
        ServerBootstrap b = new ServerBootstrap();
        b.group(group)
                .channel(NioServerSocketChannel.class)
                .childHandler(new HttpInitializer())
                .option(ChannelOption.SO_BACKLOG, 128)
                .childOption(ChannelOption.SO_KEEPALIVE, Boolean.TRUE);
        ChannelFuture f = b.bind(port).sync();       // 绑定端口,调用 ChannelFuture 的 sync() 阻塞方法等待绑定完成
        // 调用 closeFuture() 方法返回此通道关闭时的 ChannelFuture
        // 调用 ChannelFuture 的 sync() 阻塞方法直到服务端关闭链路之后才退出 main() 函数
        f.channel().closeFuture().sync();
    }
}

HTTPS

  • Java 提供了 SslContext 和 SslEngine 可以实现 SSL 加密解密,而 Netty 只需要添加 SslHandler 就能快速实现 SSL,它内部是通过 SSLEngine 来进行加密解密 ,它的流程如下:
    • 加密的入站数据被 SslHandler 拦截,并被解密
    • 普通数据传过 SslHandler
    • SslHandler 加密数据并将它传递出站
public class SSLChannelInitializer extends ChannelInitializer<SocketChannel> {

    private final SslContext sslContext;

    public SSLChannelInitializer() {
        String keyStoreFilePath = "/root/.ssl/test.pkcs12";
        String keyStorePassword = "Password@123";

        try {
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            keyStore.load(new FileInputStream(keyStoreFilePath), keyStorePassword.toCharArray());

            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            keyManagerFactory.init(keyStore, keyStorePassword.toCharArray());

            sslContext = SslContextBuilder.forServer(keyManagerFactory).build();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected void initChannel(SocketChannel ch) {
        ChannelPipeline pipeline = ch.pipeline();
        SSLEngine sslEngine = sslContext.newEngine(ch.alloc());
        pipeline
                .addLast(new SslHandler(sslEngine))
                  .addLast("decoder", new HttpRequestDecoder())
                  .addLast("encoder", new HttpResponseEncoder())
                  .addLast("aggregator", new HttpObjectAggregator(512 * 1024))
                  .addLast("handler", new HttpHandler());
        ;
    }
}

内容压缩

  • 使用 HTTP 时建议压缩数据以减少传输流量,Netty 支持“gzip” 和 “deflate”,并且提供了两个 ChannelHandler 用于压缩和解压
  • 客户端显示支持加密模式
GET /encrypted-area HTTP/1.1
Host: www.example.com
Accept-Encoding: gzip, deflate
  • 服务器端对内容进行压缩
public class HttpAggregatorInitializer extends ChannelInitializer<Channel> {

    private final boolean isClient;
    public HttpAggregatorInitializer(boolean isClient) {
        this.isClient = isClient;
    }
    @Override
    protected void initChannel(Channel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        if (isClient) {
            pipeline.addLast("codec", new HttpClientCodec()); 
            pipeline.addLast("decompressor",new HttpContentDecompressor()); 
        } else {
            pipeline.addLast("codec", new HttpServerCodec()); 
            pipeline.addLast("compressor",new HttpContentCompressor()); 
        }
    }
}

相关文章

网友评论

    本文标题:Netty4(十):实现 HttpServer 服务器 、Htt

    本文链接:https://www.haomeiwen.com/subject/yxzaqftx.html