Netty从零搭建Http服务器 终于在JNDI,RMI的驱使下,开始了网络这一块,作为Tomcat内核的netty成为开门红。
Netty从零搭建Http服务器
终于在JNDI,RMI的驱使下,开始了网络这一块,作为Tomcat内核的netty成为开门红。
- 本地服务目录如下
- 本地静态资源如下
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();
}
}