Netty从零搭建Http服务器 终于在JNDI,RMI的驱使下,开始了网络这一块,作为Tomcat内核的netty成为开门红。

Netty从零搭建Http服务器

终于在JNDI,RMI的驱使下,开始了网络这一块,作为Tomcat内核的netty成为开门红。

  • 本地服务目录如下

image-20211222143655884

  • 本地静态资源如下

image-20211222143731433

1.搭建maven工程.pom依赖如下

<!--  JSON解析  -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.2.2</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.72.Final</version>
</dependency>

2.服务启动类

package cn.http;

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

/**
 * @author 王居三木超
 * @version 1.0
 * @date 2021/12/22 11:02
 * @description TODO
 **/
public class HttpServer {

    //定义线程池
    private static NioEventLoopGroup bossGroup = new NioEventLoopGroup();
    private static NioEventLoopGroup workerGroup = new NioEventLoopGroup();

    public static void main(String[] args) throws InterruptedException {
        //服务端引导器
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        try {
            //设置EventLoop
            serverBootstrap.group(bossGroup, workerGroup)
                //设置channel
                    .channel(NioServerSocketChannel.class)
                //设置处理器
                    .childHandler(new ServerInitializer());
            //绑定端口
            ChannelFuture channelFuture = serverBootstrap.bind(8273).sync();
            //设置优雅关闭
            channelFuture.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

3.处理器编写

package cn.http;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;

/**
 * @author 王居三木超
 * @version 1.0
 * @date 2021/12/22 11:08
 * @description TODO
 **/
public class ServerInitializer extends ChannelInitializer<NioSocketChannel> {
    @Override
    protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
        ChannelPipeline pipeline = nioSocketChannel.pipeline();
        //设置http编-解码器
        pipeline.addLast("MyHttpServerCodec", new HttpServerCodec());
        //设置ChannelInboundHandler
        pipeline.addLast("ServerHandler", new ServerHandler());
    }
}

4.ChannelInboundHandler

package cn.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 org.springframework.core.io.FileSystemResource;

import java.io.IOException;

/**
 * @author 王居三木超
 * @version 1.0
 * @date 2021/12/22 11:10
 * @description TODO
 **/
public class ServerHandler extends SimpleChannelInboundHandler<HttpObject> {

    //icon路径
    private String favicon = "/favicon.ico";
    //获取resource路径下的static本地位置
    private String localPath = getClass().getResource("/static/").getFile();
    private byte[] bytes;

    private enum FileEndType {
        //Local file of request path
        HTML("html", 0),
        CSS("css", 1),
        JS("js", 2),
        ICO("ico", 3);
        public String type;
        public Integer code;

        FileEndType(String type, Integer code) {
            this.type = type;
            this.code = code;
        }
    }

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, HttpObject request) throws IOException {
        //判断是否为请求
        if (request instanceof HttpRequest) {
            //获取访问路径
            String uri = ((HttpRequest) request).uri();
            //如果为favicon.ico进行拦截
            if (favicon.equals(uri)) {
                return;
            }
            //拼接访问地址到本地的位置
            String path = localPath + uri;
            //获取文件
            FileSystemResource localFile = new FileSystemResource(path);
            //文件不存在,返回404页面
            if (!localFile.exists()) {
                path = localPath + "404.html";
                localFile = new FileSystemResource(path);
                bytes = new byte[(int) localFile.getFile().length()];
                localFile.getInputStream().read(bytes);
                try {                    
                    throw new ExceptionError(ExceptionError.error());
                } catch (ExceptionError exceptionError) {
                    //自定义返回体方法
                    channelHandlerContext.writeAndFlush(responseContent(exceptionError.getMessage()));
                }
            }
            //文件存在,正常返回资源
            bytes = new byte[(int) localFile.getFile().length()];
            localFile.getInputStream().read(bytes);
            //判断访问文件类型,进行返回
            if (path.endsWith(FileEndType.HTML.type)) {
                channelHandlerContext.writeAndFlush(responseHtml(bytes));
            } else if (path.endsWith(FileEndType.CSS.type)) {
                channelHandlerContext.writeAndFlush(responseCss(bytes));
            } else if (path.endsWith(FileEndType.JS.type)) {
                channelHandlerContext.writeAndFlush(responseJs(bytes));
            }
        }
    }

    //自定义信息返回体
    private HttpResponse responseContent(String mes) {
        //返回体内容ByteBuf
        ByteBuf content = Unpooled.copiedBuffer(mes, CharsetUtil.UTF_8);
        //生成response
        HttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1
                , HttpResponseStatus.OK, content);
        //设置返回体头步CONTENT_TYPE信息
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
        //设置返回体头步CONTENT_LENGTH信息
        response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());
        return response;
    }
    //自定义资源返回体
    private HttpResponse response(byte[] bytes, String contentType, HttpResponseStatus status) {
        ByteBuf content = Unpooled.copiedBuffer(bytes);
        HttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1
                , status, content);
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, contentType);
        response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());
        return response;
    }

    private HttpResponse responseHtml(byte[] bytes) {
        return response(bytes, "text/html", HttpResponseStatus.OK);
    }

    private HttpResponse responseHtml(byte[] bytes, HttpResponseStatus status) {
        return response(bytes, "text/html", status);
    }

    private HttpResponse responseJs(byte[] bytes) {
        return response(bytes, "text/javascript", HttpResponseStatus.OK);
    }

    private HttpResponse responseJs(byte[] bytes, HttpResponseStatus status) {
        return response(bytes, "text/javascript", status);
    }

    private HttpResponse responseCss(byte[] bytes) {
        return response(bytes, "text/css", HttpResponseStatus.OK);
    }

    private HttpResponse responseCss(byte[] bytes, HttpResponseStatus status) {
        return response(bytes, "text/css", status);
    }
}

5.异常类如下

package cn.http;

import cn.hutool.json.JSONObject;

/**
 * @author 王居三木超
 * @version 1.0
 * @date 2021/12/22 13:06
 * @description TODO
 **/
public class ExceptionError extends Exception {


    private String message;

    public ExceptionError(String message) {
        super(message);
        this.message = message;
    }

    @Override
    public String getMessage() {
        return message;
    }

    public static String error() {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("version", "1.1");
        jsonObject.put("author", "wjsmc");
        jsonObject.put("localwebsite", "http://www.openpool.cn/");
        return jsonObject.toString();
    }
}

6.测试访问

原文地址:https://www.cnblogs.com/hmcjsc/p/15719504.html