Netty6-快速入门HTTP服务

319 阅读3分钟

我正在参加「掘金·启航计划」

快速入门实例-HTTP服务

  1. 实例要求:使用IDEA 创建Netty项目
  2. Netty 服务器在 6668 端口监听,浏览器发出请求
    "http://localhost:6668/ "
  3. 服务器可以回复消息给客户端 "Hello! 我是服务器 5 " , 并
    对特定请求资源进行过滤.
  4. 目的:Netty 可以做Http服务开发,并且理解Handler实例
    和客户端及其请求的关系.

1.HttpServer

package com.atguigu.netty.NettyHttp;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * @Description: HTTP服务
 * @author: Freedom
 * @QQ: 1556507698
 * @date:2022/6/24 10:41
 */

public class HTTPServer {
    public static void main(String[] args) throws Exception{
        //1. 创建两个线程组 bossGroup 和 workerGroup
        //2. bossGroup 只是处理连接请求 , 真正的和客户端业务处理,会交给 workerGroup完成
        //3. 两个都是无限循环
        //4. bossGroup 和 workerGroup 含有的子线程(NioEventLoop)的个数
        //   默认实际 cpu核数 * 2
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup(); //8

        try {
            //2.创建服务器端的启动对象,配置参数
            ServerBootstrap serverBootStrap = new ServerBootstrap();

            //3.设置线程组
            serverBootStrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new HTTPServerInitializer());

            System.out.println("HTTP 服务器启动成功");
            //4.绑定端口号
            ChannelFuture channelFuture = serverBootStrap.bind(1192).sync();

            //5.监听事件关闭
            channelFuture.channel().closeFuture().sync();

        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }
}

HTTPServerInitializer 通道初始化对象

创建一个通道初始化对象 并给WorkGroup的 EventLoop 对应的管道设置处理器 可以是Netty提供的 也可以是自定义的

package com.atguigu.netty.NettyHttp;

import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;

/**
 * @Description: ···
 * @author: Freedom
 * @QQ: 1556507698
 * @date:2022/6/24 10:43
 */

public class HTTPServerInitializer extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        //向管道加入处理器


        //得到管道
        ChannelPipeline pipeline = ch.pipeline();

        //加入netty提供的HttpServerCodec ; code =》 coder - decoder
        //netty 提供的处理http的编解码器
        pipeline.addLast("my http server codec",new HttpServerCodec());

        //增加自定义的处理器Handle
        pipeline.addLast("自定义Handler",new HTTPServerHandle());
    }
}

Http ServerHandle

package com.atguigu.netty.NettyHttp;

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;

/**
 * @Description: ···
 * @author: Freedom
 * @QQ: 1556507698
 * @date:2022/6/24 10:41
 */

public class HTTPServerHandle extends SimpleChannelInboundHandler<HttpObject> {

    /**
     * SimpleChannelInboundHandler<I> extends ChannelInboundHandlerAdapter
     * 当有读取事件时触发此方法
     * HttpObject 表示客户端和服务器端相互通讯的数据被封装成HttpObject
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {

        //判断msg是不是一个HttpRequest请求
        if (msg instanceof HttpObject){
            System.out.println("msg 类型="+ msg.getClass());
            System.out.println("客户端地址="+ ctx.channel().remoteAddress());

            //回复信息给浏览器 得做成Http协议的形式

            //创建bytebuf
            ByteBuf cotent = Unpooled.copiedBuffer(" ByteBuf cotent = Unpooled.copiedBuffer(\"我是服务器返回消息\", CharsetUtil.UTF_8);\n", CharsetUtil.UTF_8);


            //构造一个http的响应 httpResponse 参数:1.协议版本 2.响应状态码3.内容
            FullHttpResponse httpResponse = new DefaultFullHttpResponse(
                    HttpVersion.HTTP_1_1,
                    HttpResponseStatus.OK,
                    cotent);

            //设置内容类型
            httpResponse.headers().set(HttpHeaderNames.CONTENT_TYPE,"text/plain");
            //设置返回数据长度
            httpResponse.headers().set(HttpHeaderNames.CONTENT_LENGTH,cotent.readableBytes());

            //将构建好的httpResponse 返回
            ctx.writeAndFlush(httpResponse);
        }

    }

}

服务启动后访问监听的浏览器端口即可得到返回消息

此时控制台打印

HTTP 服务器启动成功
客户端地址=/0:0:0:0:0:0:0:1:60504
客户端地址=/0:0:0:0:0:0:0:1:60504
客户端地址=/0:0:0:0:0:0:0:1:60505
客户端地址=/0:0:0:0:0:0:0:1:60505
客户端地址=/0:0:0:0:0:0:0:1:60509
客户端地址=/0:0:0:0:0:0:0:1:60509
客户端地址=/0:0:0:0:0:0:0:1:60510
客户端地址=/0:0:0:0:0:0:0:1:60510

会发现浏览器发送了两次请求
在这里插入图片描述
一次是请求网站,一次是请求网站的图标

进行请求过滤

//获取到消息
            HttpRequest httpRequest = (HttpRequest) msg;
            //获取uri 统一资源定位
            URI uri = new URI(httpRequest.uri());

            //如果uri的path路径包含请求图标
            if ("/favicon.ico".equals(uri.getPath())){
                System.out.println("请求了图标资源,不做相应");
                return;
            }