【Netty】使用 Netty 开发 HTTP 服务器

本文详细介绍如何使用Netty开发HTTP服务器,包括代码分析与实践。文章覆盖了HTTP服务器的开发需求,对比TCP服务器,深入解析ChannelInitializer设置和自定义业务逻辑处理器。并通过具体代码示例展示了服务器主程序和业务逻辑处理类的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >





一、 HTTP 服务器开发



HTTP 服务器案例需求 :


① HTTP 服务器端 : 在服务器端使用 Netty 开发 HTTP 服务器 , 该 HTTP 服务器监听 8888 端口 ;

② 浏览器操作 : 浏览器中访问 http://127.0.0.1:8888/ 地址 , 或 http://localhost:8888/ 地址 , HTTP 服务器返回 “Hello Client” 信息 ;

③ 信息过滤 : 客户端浏览器请求 HTTP 服务时 , 涉及返回字符串信息时 , 还会返回其它额外信息 , 如网站图标等信息 , 现在我们只要服务器返回的字符串信息 , 屏蔽其它信息 , 需要开发信息过滤机制 ;





二、 HTTP 服务器代码分析




1 . Netty 开发 HTTP 服务器与 TCP 服务器对比


HTTP 协议的服务器与 TCP 协议的服务器程序区别在于设置的 ChannelInitializer 和 Handler 中对数据的处理方式不同 ; 其它操作基本类似 , 如 :

  • 创建 BossGroup 和 WorkerGroup 两个线程池 ;
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
  • 创建 ServerBootstrap 并进行配置 , 配置项除了 ChannelInitializer 与 Handler 都一样 ;
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
                // HTTP 协议与 TCP 协议不同
        );
  • 绑定端口 ;
 channelFuture = bootstrap.bind(8888).sync();

相同的不再赘述 , 下面针对不同的技术点进行详细描述 ;



2 . ChannelInitializer 设置


1 . 首先要获取该客户端连接对应的管道 : ChannelPipeline pipeline = ch.pipeline() ;


2 . 设置 HTTP 协议的编解码器 : pipeline.addLast(“HttpServerCodec” , new HttpServerCodec()) , 为管道加入 HTTP 协议的编解码器 HttpServerCodec ;


3 . 设置业务逻辑处理器 Handler : pipeline.addLast(“HTTPServerHandler”, new HTTPServerHandler()) , 该业务逻辑处理器是用户自定义的 ;


4 . 代码示例 :

ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup) 
        .channel(NioServerSocketChannel.class)  
        .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());
                    }
                }
        // 核心示例代码 ---------------------------------------------------------------------
        );


3 . 自定义业务逻辑处理器


1 . 类继承 : 自定义的业务逻辑处理器 , 继承 SimpleChannelInboundHandler<HttpObject> 类 ;


2 . 核心方法 : protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception 方法是业务逻辑处理的核心方法 , 必须由用户自己实现 ;


3 . 判定协议请求类型 : 判定 HttpObject msg 类型是否是 HttpRequest 类型 , 如果是 , 说明该请求是 HTTP 请求 ;

if(msg instanceof HttpRequest){ //判断该  HttpObject msg 参数是否是 Http 请求
	//...
}

4 . 返回 HTTP 响应数据 :


① 创建 HTTP 响应对象 : DefaultFullHttpResponse , 设置 HTTP 协议的版本 , 响应状态 , 及返回数据 ;

// 准备给客户端浏览器发送的数据
ByteBuf byteBuf = Unpooled.copiedBuffer("Hello Client", CharsetUtil.UTF_8);

// 设置 HTTP 版本, 和 HTTP 的状态码, 返回内容
DefaultFullHttpResponse defaultFullHttpResponse =
        new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, byteBuf);

② 设置 HTTP 请求头 : 设置内容类型 , 和内容长度 ;

// 设置 HTTP 请求头
// 设置内容类型是文本类型
defaultFullHttpResponse.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
// 设置返回内容的长度
defaultFullHttpResponse.headers().set(
        HttpHeaderNames.CONTENT_LENGTH,
        byteBuf.readableBytes());

③ 写出 HTTP 数据 : 调用 ChannelHandlerContext ctx 参数的 writeAndFlush 即可写出 HTTP 响应数据到客户端 ;

// 写出 HTTP 数据
ctx.writeAndFlush(defaultFullHttpResponse);

5 . 代码示例 : 查看下一节的 服务器业务逻辑处理类 代码 ;





三、 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;

/**
 * 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() + " 客户端请求数据 ... ");

            // 准备给客户端浏览器发送的数据
            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 . 执行结果


① 启动服务器 : 启动 HTTP 服务器 , 监听 8888 端口 , 只接收 HTTP 请求 ;

在这里插入图片描述


② 浏览器访问服务器 : 浏览器中输入 http://127.0.0.1:8888/ 地址 , 即可访问 Netty HTTP 服务器 , 服务器返回 Hello Client 字符串信息 ;

在这里插入图片描述


③ 服务器端日志 :

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值