点一下关注吧!!!非常感谢!!持续更新!!!
🚀 AI篇持续更新中!(长期更新)
目前2025年06月13日更新到:
AI炼丹日志-28 - Audiblez 将你的电子书epub转换为音频mp3 做有声书,持续打造实用AI工具指南!📐🤖
💻 Java篇正式开启!(300篇)
目前2025年06月11日更新到:
Java-44 深入浅出 Nginx - 底层进程机制 Master Worker 机制原理 常用指令
MyBatis 已完结,Spring 已完结,深入浅出助你打牢基础!
📊 大数据板块已完成多项干货更新(300篇):
包括 Hadoop、Hive、Kafka、Flink、ClickHouse、Elasticsearch 等二十余项核心组件,覆盖离线+实时数仓全栈!
目前2025年06月13日更新到:
大数据-278 Spark MLib - 基础介绍 机器学习算法 梯度提升树 GBDT案例 详解
MiniCat
基本介绍
我们要手写实现一个 Mini 版的 Tomcat,我们要实现的是,作为一个服务器软件,可以通过我们的浏览器客户端发送HTTP请求,Minicat处理之后,处理结果可以返回给浏览器的客户端。
● 提供服务,接受请求(Socket通信)
● 请求信息封装成 Request 对象(Response对象)
● 客户端请求资源,资源分为静态资源(HTML)和动态资源(Servlet)
● 资源返回客户端浏览器
Response 请求响应类
定义了一个名为 Response 的公共类,用于封装向客户端发送数据(如 HTML、文本等)的逻辑。
public class Response {private OutputStream outputStream;public Response() {}public Response(OutputStream outputStream) {this.outputStream = outputStream;}public void output(String content) throws IOException {outputStream.write(content.getBytes());}public void outputHtml(String path) throws IOException {String absoluteResourcePath = StaticResourceUtil.getAbsolutePath(path);File file = new File(absoluteResourcePath);if (file.exists() && file.isFile()) {StaticResourceUtil.outputStaticResource(new FileInputStream(file), outputStream);} else {output(HttpProtocoUtil.getHttpHeader404());}}
}
OutputStream 是 Java I/O 的基类,用于向外部(如客户端的 socket)输出字节数据。它的典型使用场景是在 Web 服务中向浏览器写入响应内容。
对于 output 方法,这个方法将一个字符串 content 转换成字节数组,然后写入 outputStream。实质上,它把一段内容(如 HTML 文本)直接返回给客户端。
其中用到的工具类,StaticResourceUtil.getAbsolutePath(path) 是一个工具方法(未给出具体代码),通常作用是将相对路径转换为项目中的绝对路径。
对于 Response 类来说,主要的职责就是:
- 封装输出流:把 OutputStream 作为类的核心资源,用于向客户端写数据
- 文本输出:可以直接输出一个字符串到客户端(如返回 HTTP 响应头和正文)
- HTML输出:从文件系统中读取静态 HTML 文件并写回客户端
- 404处理:文件不存在时自动返回标准的 404 响应
Servlet 接口
public interface Servlet {void init() throws Exception;void destroy() throws Exception;void service(Request request, Response response) throws Exception;}
StaticResourceUtil 静态资源
public class StaticResourceUtil {public static String getAbsolutePath(String path) {String absolutePath = StaticResourceUtil.class.getResource("/").getPath();return absolutePath.replace("\\\\", "/") + path;}public static void outputStaticResource(InputStream inputStream, OutputStream outputStream) throws IOException {int count = 0;while (count == 0) {count = inputStream.available();}int resourceSize = count;outputStream.write(HttpProtocoUtil.getHttpHeader200(resourceSize).getBytes());long written = 0;int byteSize = 1024;byte[] bytes = new byte[byteSize];while (written < resourceSize) {if (written + byteSize > resourceSize) {byteSize = (int) (resourceSize - written);bytes = new byte[byteSize];}inputStream.read(bytes);outputStream.write(bytes);outputStream.flush();written += byteSize;}}}
获取资源的绝对路径:getAbsolutePath
- StaticResourceUtil.class.getResource(“/”) 会获取到当前类所在的类路径(classpath 根目录)。
- .getPath() 获取的是该目录的实际路径(如:/home/user/project/target/classes/)。
- replace(“\\”, “/”) 是为了处理 Windows 路径中的反斜杠转为正斜杠,但这里写得 有问题(见下文)。
- 最终将 path 拼接在 classpath 后,返回静态资源的绝对路径。
输出静态资源内容:outputStaticResource
- inputStream.available() 试图获取文件长度(单位为字节数);
- 但是用 while (count == 0) 等待它非 0 的方式是 不稳定的。
- available() 并不等价于“文件大小”,它是“当前可读字节数”,不适合用于预估资源总长度。
逐段读取并写出数据: “while (written < resourceSize)”:
- 分块读写,每次最多读取 1024 字节;
- 如果最后一块不足 1024 字节,动态调整 byteSize 和 bytes[] 大小;
- 每读取一块,立即写入并 flush(即时响应给客户端)。
整体的小总结:
- 类名:StaticResourceUtil,工具类
- 功能:获取资源路径、输出文件内容到 HTTP
- 亮点:模拟 Web Server 发送 HTML 等静态文件
- 关键问题:误用 available()、不检查读取字节数、频繁分配数组
- 优化方向:用 readAllBytes() 或 while(read != -1) 模式代替原始读取逻辑
WzkServlet 实现类
这里就是对之前的内容的使用,建立了一自己的Servlet,然后启动服务即可!
public class WzkServlet extends HttpServlet {@Overridepublic void doGet(Request request, Response response) {String content = "<h1>WzkServlet GET Hello World!</h1>";try {response.output(HttpProtocoUtil.getHttpHeader200(content.getBytes().length) + content);} catch (Exception e) {e.printStackTrace();}}@Overridepublic void doPost(Request request, Response response) {String content = "<h1>WzkServlet POST Hello World!</h1>";try {response.output(HttpProtocoUtil.getHttpHeader200(content.getBytes().length) + content);} catch (Exception e) {e.printStackTrace();}}@Overridepublic void init() throws Exception {}@Overridepublic void destroy() throws Exception {}}
资源文件
index.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>wzk-icu</title>
</head>
<body>
<h1>wzk-icu 测试页面!
</h1>
</body>
</html>
web.xml
<?xml version="1.0" encoding="UTF-8" ?>
<web-app><servlet><servlet-name>wzkicu</servlet-name><servlet-class>icu.wzk.WzkServlet</servlet-class></servlet><servlet-mapping><servlet-name>wzkicu</servlet-name><url-pattern>/wzkicu</url-pattern></servlet-mapping>
</web-app>
启动测试
目前我们的代码概览是这样的:
我们启动服务:
接着我们访问8080端口:
我们可以看到控制台输出了对应的内容