【Netty系列】实现HTTP协议

目录

服务端实现(支持HTTP/1.1)

客户端实现(支持HTTP GET/POST)

代码结构解析

服务端核心组件:

客户端核心组件:

协议处理流程对比

测试方法

关键协议实现细节

性能优化建议


以下是基于Netty实现HTTP协议的完整客户端和服务端代码,包含详细的协议处理逻辑:


服务端实现(支持HTTP/1.1)

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.Unpooled;
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.*;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;

public class HttpServer {

    public static void main(String[] args) throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class)
             .childHandler(new ChannelInitializer() {
                 @Override
                 protected void initChannel(SocketChannel ch) {
                     ChannelPipeline p = ch.pipeline();
                     // HTTP协议编解码器
                     p.addLast(new HttpServerCodec());
                     // 消息聚合器(最大聚合5MB)
                     p.addLast(new HttpObjectAggregator(5 * 1024 * 1024));
                     // 业务处理器
                     p.addLast(new HttpServerHandler());
                 }
             })
             .option(ChannelOption.SO_BACKLOG, 128)
             .childOption(ChannelOption.SO_KEEPALIVE, true);

            ChannelFuture f = b.bind(8080).sync();
            System.out.println("HTTP服务端启动成功,端口:8080");
            f.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    private static class HttpServerHandler extends SimpleChannelInboundHandler {
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest req) {
            try {
                // 1. 解析请求
                String uri = req.uri();
                HttpMethod method = req.method();
                String content = req.content().toString(CharsetUtil.UTF_8);

                // 2. 构建响应
                String responseMsg = "处理成功\nMethod: " + method + "\nURI: " + uri + "\nBody: " + content;
                FullHttpResponse response = new DefaultFullHttpResponse(
                        HttpVersion.HTTP_1_1,
                        HttpResponseStatus.OK,
                        Unpooled.copiedBuffer(responseMsg, CharsetUtil.UTF_8));

                // 3. 设置响应头
                response.headers()
                        .set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8")
                        .setInt(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes())
                        .set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);

                // 4. 发送响应
                ctx.writeAndFlush(response);
            } finally {
                ReferenceCountUtil.release(req);
            }
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            cause.printStackTrace();
            ctx.close();
        }
    }
}

客户端实现(支持HTTP GET/POST)

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;

public class HttpClient {

    public static void main(String[] args) throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group)
             .channel(NioSocketChannel.class)
             .handler(new ChannelInitializer() {
                 @Override
                 protected void initChannel(Channel ch) {
                     ChannelPipeline p = ch.pipeline();
                     p.addLast(new HttpClientCodec());
                     p.addLast(new HttpObjectAggregator(1024 * 1024));
                     p.addLast(new HttpClientHandler());
                 }
             });

            // 连接到服务端
            ChannelFuture f = b.connect("localhost", 8080).sync();
          
            // 构建HTTP请求
            FullHttpRequest request = new DefaultFullHttpRequest(
                    HttpVersion.HTTP_1_1,
                    HttpMethod.POST,
                    "/api/data",
                    Unpooled.copiedBuffer("请求数据", CharsetUtil.UTF_8));
          
            // 设置请求头
            request.headers()
                   .set(HttpHeaderNames.HOST, "localhost")
                   .set(HttpHeaderNames.CONTENT_TYPE, "text/plain")
                   .setInt(HttpHeaderNames.CONTENT_LENGTH, request.content().readableBytes())
                   .set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);

            // 发送请求
            f.channel().writeAndFlush(request);
          
            // 等待连接关闭
            f.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }

    private static class HttpClientHandler extends SimpleChannelInboundHandler {
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, FullHttpResponse response) {
            try {
                // 解析响应
                String content = response.content().toString(CharsetUtil.UTF_8);
                System.out.println("收到响应:");
                System.out.println("状态码:" + response.status().code());
                System.out.println("内容:\n" + content);
            } finally {
                ReferenceCountUtil.release(response);
            }
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            cause.printStackTrace();
            ctx.close();
        }
    }
}

代码结构解析

服务端核心组件:
  1. HttpServerCodec:组合了HttpRequestDecoderHttpResponseEncoder
  2. HttpObjectAggregator:将分片的HTTP消息聚合成完整的FullHttpRequest
  3. 自定义处理器:处理完整的HTTP请求并生成响应
客户端核心组件:
  1. HttpClientCodec:组合了HttpResponseDecoderHttpRequestEncoder
  2. HttpObjectAggregator:聚合服务端的响应消息
  3. 自定义处理器:处理完整的HTTP响应

协议处理流程对比

处理阶段

服务端处理流程

客户端处理流程

协议解析

解码请求(HttpRequestDecoder)

解码响应(HttpResponseDecoder)

消息聚合

聚合请求(HttpObjectAggregator)

聚合响应(HttpObjectAggregator)

业务处理

生成HTTP响应

处理HTTP响应

协议封装

编码响应(HttpResponseEncoder)

编码请求(HttpRequestEncoder)


测试方法

  1. 启动服务端
java HttpServer
  1. 测试服务端(使用curl)
curl -X POST -d "test data" http://localhost:8080/api/data
  1. 启动客户端
java HttpClient

关键协议实现细节

  1. Keep-Alive处理
// 服务端设置
response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);

// 客户端设置
request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
  1. 内容长度自动计算
// 自动设置Content-Length
HttpUtil.setContentLength(response, response.content().readableBytes());
  1. 分块传输支持
// 服务端开启分块传输
response.headers().set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED);
  1. 100 Continue处理
if (HttpUtil.is100ContinueExpected(request)) {
    ctx.writeAndFlush(new DefaultFullHttpResponse(
        HttpVersion.HTTP_1_1, 
        HttpResponseStatus.CONTINUE));
}

性能优化建议

  1. 对象池化
// 开启Netty对象池
System.setProperty("io.netty.allocator.type", "pooled");
  1. 零拷贝优化
// 文件传输使用FileRegion
FileRegion region = new DefaultFileRegion(file, 0, file.length());
ctx.writeAndFlush(region);
  1. 连接池管理(客户端):
// 使用ConnectionPool
Bootstrap bootstrap = new Bootstrap();
GenericObjectPool pool = new GenericObjectPool<>(
    new ChannelPooledObjectFactory(bootstrap));

该实现完整覆盖了HTTP/1.1协议的核心功能,包括:

  • 请求/响应头解析
  • 内容长度自动处理
  • Keep-Alive连接复用
  • 分块传输编码
  • 100 Continue机制
  • 异常处理流程

可根据具体业务需求扩展处理逻辑(如添加路由、参数解析、Session管理等)。

你可能感兴趣的:(Netty,http,网络编程,netty)