【学习笔记】手写Tomcat 二

目录

响应静态资源

HTTP协议请求格式

1. 解析请求信息

创建解析请求类 HttpRequest

2. 创建静态资源目录 webs

3. 封装响应信息

创建静态资源处理器 StaticResourceHandler

创建响应类 HttpResponse

然后就可以调用响应类了

测试

静态资源的路径说明

作业

1. 绘制 请求解析类 HttpRequest 和响应类 HttpResponse 的封装流程图

2. 优化客户端的连接


响应静态资源

在昨天的基础上,再进一步优化,能够响应HTML文件,图片等静态资源

在响应静态资源之前,我们要先确定客户端请求的是哪个静态资源,是HTML页面还是图片呢?

我们先打印一下客户端的请求信息

HTTP协议请求格式

可以看到,第一行也就是 请求行 ,它有请求方法,请求URL和请求协议

请求URL就是需要请求的资源

1. 解析请求信息

那如何拿到 请求的URL呢?然后根据请求URL响应对应的资源

我们看一下 HTTP协议的请求格式,不难发现,都是以回车符换行符结尾

以 回车符和换行符进行分割,然后再根据 空格 分割就可以拿到请求URL了

创建解析请求类 HttpRequest

package com.shao.net;import java.util.HashMap;public class HttpRequest {/*** 请求信息*/private String msg;/*** 请求行*/private String requestLine;/*** 请求行的请求方法*/private String requestMethod;/*** 请求行的请求URI,请求路径*/private String requestURI;/*** 请求行的请求模块,例如:/index.html?a=1 请求模块为:/index.html*/private String requestModule;/*** 请求行的请求协议*/private String requestProtocol;/*** 存储请求头参数*/private HashMap<String, String> requestHeaderParams = new HashMap<>();/*** 存储请求体参数*/private HashMap<String, String> requestBodyParams = new HashMap<>();/*** 构造函数*/public HttpRequest(String msg) {this.msg = msg;// 根据HTTP协议格式 分割请求信息String[] requestArr = msg.split("\r\n");// 把数组中的第一个元素赋值给请求行requestLine = requestArr[0];// 1. 解析请求行parseRequestLine();// 2. 解析请求头parseRequestHeader(requestArr);// 3. 解析请求体parseRequestBody();}// 1. 解析请求行private void parseRequestLine() {// 把请求行按空格分割String[] requestParts = requestLine.split(" ");// 请求方法requestMethod = requestParts[0];// 请求资源路径requestURI = requestParts[1];// 请求协议requestProtocol = requestParts[2];// 如果请求方法是GET,则根据 ? 号分割,获取请求模块,例如:/index.html?a=1 请求模块为:/index.htmlString[] split = requestURI.split("\\?");requestModule = split[0];System.out.println("请求方法:" + requestMethod);System.out.println("请求uri:" + requestURI);System.out.println("请求协议:" + requestProtocol);System.out.println("请求模块:" + requestModule);}// 2. 解析请求头private void parseRequestHeader(String[] requestArr) {// 检查请求数组,判断是否为空,判断有没有请求头if (requestArr == null || requestArr.length <= 1) {return;}// 遍历请求数组,从第二个元素开始,因为第一个元素是请求行for (int i = 1; i < requestArr.length; i++) {// 判断是否为空,如果为空,表示请求头结束,因为 HTTP 协议中,请求头和请求体之间有2个回车符 换行符String headerLine = requestArr[i];if (headerLine.length() == 0) {break;}// 把请求头按 :  分割,获取请求头参数// 注意:这里需要使用 ": ",而不是 ":"String[] headerParts = headerLine.split(": ");// 判断请求头的格式是否正确if (headerParts.length >= 2) {requestHeaderParams.put(headerParts[0], headerParts[1]);}}System.out.println("请求头参数:" + requestHeaderParams);}// 3. 解析请求体private void parseRequestBody() {// POST 方法的请求参数是在请求体,GET 方法的请求参数在请求行if (this.requestMethod.equalsIgnoreCase("POST")) {// 分割请求信息String[] split = msg.split("\r\n\r\n");// 如果分割后的长度 >= 2,表示有请求体if (split.length >= 2) {// 分割请求体splitRequestBody(split[1]);}} else if (this.requestMethod.equalsIgnoreCase("GET")) {// 把请求行按空格分割,获取请求参数String[] requestLineParts = this.requestLine.split(" ");String[] split = requestLineParts[1].split("\\?");if (split.length >= 2) {// 分割请求体splitRequestBody(split[1]);}}System.out.println("请求体参数:" + requestBodyParams);}private void splitRequestBody(String requestBody) {// 分割请求参数,例如:a=1&b=2&c=3String[] requestBodyParts = requestBody.split("&");// 遍历请求体for (int i = 0; i < requestBodyParts.length; i++) {String part = requestBodyParts[i];// 把请求参数按 = 分割,获取键值对,最多分割2部分,如果有多个 = ,只保留第一个 = 之前和之后的部分String[] keyValue = part.split("=", 2);if (keyValue.length == 2) {requestBodyParams.put(keyValue[0], keyValue[1]);} else {System.out.println("警告:非法格式的请求体:" + part);}}}/*** 获取* @return msg*/public String getMsg() {return msg;}/*** 设置* @param msg*/public void setMsg(String msg) {this.msg = msg;}/*** 获取* @return requestLine*/public String getRequestLine() {return requestLine;}/*** 设置* @param requestLine*/public void setRequestLine(String requestLine) {this.requestLine = requestLine;}/*** 获取* @return requestMethod*/public String getRequestMethod() {return requestMethod;}/*** 设置* @param requestMethod*/public void setRequestMethod(String requestMethod) {this.requestMethod = requestMethod;}/*** 获取* @return requestURI*/public String getRequestURI() {return requestURI;}/*** 设置* @param requestURI*/public void setRequestURI(String requestURI) {this.requestURI = requestURI;}/*** 获取* @return requestModule*/public String getRequestModule() {return requestModule;}/*** 设置* @param requestModule*/public void setRequestModule(String requestModule) {this.requestModule = requestModule;}/*** 获取* @return requestProtocol*/public String getRequestProtocol() {return requestProtocol;}/*** 设置* @param requestProtocol*/public void setRequestProtocol(String requestProtocol) {this.requestProtocol = requestProtocol;}/*** 获取* @return requestHeaderParams*/public HashMap<String, String> getRequestHeaderParams() {return requestHeaderParams;}/*** 设置* @param requestHeaderParams*/public void setRequestHeaderParams(HashMap<String, String> requestHeaderParams) {this.requestHeaderParams = requestHeaderParams;}/*** 获取* @return requestBodyParams*/public HashMap<String, String> getRequestBodyParams() {return requestBodyParams;}/*** 设置* @param requestBodyParams*/public void setRequestBodyParams(HashMap<String, String> requestBodyParams) {this.requestBodyParams = requestBodyParams;}public String toString() {return "HttpRequest{msg = " + msg + ", requestLine = " + requestLine + ", requestMethod = " + requestMethod + ", requestURI = " + requestURI + ", requestModule = " + requestModule + ", requestProtocol = " + requestProtocol + ", requestHeaderParams = " + requestHeaderParams + ", requestBodyParams = " + requestBodyParams + "}";}
}

调用 HttpRequest 解析请求信息

游览器发送请求

打印解析后的请求信息,可以看到请求的静态资源是 /index.html

拿到了请求uri,我们就可以根据 请求uri 响应静态资源了

那这个静态资源放在哪儿呢?

2. 创建静态资源目录 webs

创建一个 HTML 文件,内容先写英文,写中文需要在响应头加上 UTF-8 字符编码

那现在 index.html 有了,怎么响应给客户端呢?

读取 index.html 文件,将读取到的内容发送到游览器,游览器会自动渲染页面

3. 封装响应信息

把响应信息的代码拿出来,单独放到一个类里,这个类就只需要响应数据

这样可以降低代码的耦合度

在创建响应类之前有两个问题

1. 怎么确定请求的是静态资源?动态资源一般需要操作数据库

2. 响应的媒体类型怎么写?

如果响应的是图片,那么就需要修改为 Content-Type:image/jpeg 或 image/png

所以,就需要动态的判断响应的静态资源是什么媒体类型。

创建静态资源处理器 StaticResourceHandler

package com.shao.net;import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;public class StaticResourceHandler {// 常见静态资源扩展名private static final String[] staticExtensions = new String[]{".html", ".css", ".js", ".jpg", ".png", ".gif", ".ico", ".svg", ".pdf", ".txt"};/*** 判断是否静态资源* 如果是静态资源则返回 true ,否则返回 false*/public static boolean isLikelyStaticResource(String fileName) {// 检查文件的扩展名是否存在于静态资源扩展名数组中for (String ext : staticExtensions) {if (fileName.endsWith(ext)) {return true;}}return false;}/*** 根据文件路径获取文件内容*/public static byte[] getFileContents(String filePath) {// 1. 获取文件对象File file = new File(filePath);// 2. 判断文件是否存在if (!file.exists()) {return null;}// 3. 获取文件内容// 定义一个字节数组,数组大小根据文件大小确定byte[] fileContents = new byte[0];try {FileInputStream fis = new FileInputStream(file);// fis.available() 是读取的文件的字节数fileContents = new byte[fis.available()];// 读取文件存放到 fileContents 数组中fis.read(fileContents);// 关闭文件输入流fis.close();} catch (Exception e) {e.printStackTrace();}return fileContents;}/*** 获取文件的媒体类型*/public static String getFileMimeType(String filePath) {// 1 获取文件对象File file = new File(filePath);// 2 判断文件是否存在if (!file.exists()) {return "text/html";}// 3 获取文件的媒体类型String fileType = null;try {fileType = Files.probeContentType(Paths.get(filePath));} catch (IOException e) {e.printStackTrace();}return fileType;}}

创建响应类 HttpResponse

package com.shao.net;import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;public class HttpResponse {/*** 输出流*/private OutputStream os;/*** 解析请求信息的对象*/private HttpRequest httpRequest;public HttpResponse(OutputStream os, HttpRequest httpRequest) {this.os = os;this.httpRequest = httpRequest;}public void response(String filePath) {//判断请求的是否为静态文件if (StaticResourceHandler.isLikelyStaticResource(httpRequest.getRequestModule())) {// 获取静态资源一般是 GET 请求方法if (httpRequest.getRequestMethod().equals("GET")) {// 响应静态资源responseStaticResource(filePath);}} else {// 处理动态请求System.out.println("请求动态资源");}}/*** 响应静态资源*/private void responseStaticResource(String filePath) {// 读取文件byte[] fileContents = StaticResourceHandler.getFileContents(filePath);// 判断文件是否存在,不存在则返回 404 的页面if (fileContents == null) {try {FileInputStream fis = new FileInputStream("webs/pages/not_Found404.html");fileContents = new byte[fis.available()];fis.read(fileContents);fis.close();} catch (Exception e) {e.printStackTrace();}}// 响应协议String protocol = httpRequest.getRequestProtocol();// 文件媒体类型String fileMimeType = StaticResourceHandler.getFileMimeType(filePath);try {os.write((protocol + " 200 OK\r\n").getBytes());os.write(("Content-Type: " + fileMimeType + "\r\n").getBytes());os.write(("Content-Length: " + fileContents.length + "\r\n").getBytes());os.write("\r\n".getBytes());os.write(fileContents);os.flush();System.out.println("响应成功");os.close();} catch (IOException e) {e.printStackTrace();}}
}

然后就可以调用响应类了

测试

静态资源的路径说明

可以直接复制文件的相对路径 

作业

1. 绘制 请求解析类 HttpRequest 和响应类 HttpResponse 的封装流程图

2. 优化客户端的连接

现在只有主线程处理客户端连接,如果同一时刻有多个客户端发起连接,同一时间只能处理一个客户端的连接,这样效率不高,怎么处理?

解决方案是来一个客户端连接就创建一条线程去处理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/424444.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JNI 详细介绍

一 介绍 java调⽤c&#xff0c;c代码可以通过JNIEnv执行java代码。 安卓NDK 已经对JNI环境进行了集成&#xff0c;我们可以通过android studio来快速搭建一个项目。 二 项目搭建 打开android studio 创建工程&#xff0c;创建工程选择模板Native C 三 模板格式介绍 生成的…

非关系型数据库Redis

文章目录 一&#xff0c;关系型数据库和非关系型数据可区别1.关系型数据库2.非关系型数据库3.区别3.1存储方式3.2扩展方式3.2事务性的支持 二&#xff0c;非关系型数据为什么产生三&#xff0c;Redis1.Redis是什么2.Redis优点3.Redis适用范围4. Redis 快的原因4.1 基于内存运行…

直播标准权威发布,阿里云RTS获首批卓越级评估认证

近期举办的2024“可信云大会”上&#xff0c;中国信通院正式发布了2024年上半年音视频领域最新评估结果。阿里云超低延时直播&#xff0c;以首批卓越级&#xff0c;通过中国信通院超低延时直播性能及服务质量分级测试。 标准发布&#xff0c;权威量化直播体验质量 从直播元年发…

神经网络通俗理解学习笔记(0) numpy、matplotlib

Numpy numpynumpy 基本介绍Ndarray对象及其创建Numpy数组的基础索引numpy数组的合并与拆分&#xff08;重要&#xff09;numpy数组的矩阵运算Numpy数组的统计运算numpy中的arg运算numpy中的神奇索引和比较 Matplotlib numpy numpy 基本介绍 numpy 大多数机器学习库都用了这个…

【Echarts】使用多横坐标轴展示近十五天天气预报

现在手机都有天气app,使用echarts展示十五天天气预报的需要你遇到过这样离大谱的需求吗&#xff1f;如果没有或许你能从中找到些许思路。 效果 看效果是不是有点那么个意思,开局一张图,代码全靠ctrl c。不多说上代码。 vue模板引擎代码 <template><div ref"xA…

从头开始学MyBatis—02基于xml和注解分别实现的增删改查

首先介绍此次使用的数据库结构&#xff0c;然后引出注意事项。 通过基于xml和基于注解的方式分别实现了增删改查&#xff0c;还有获取参数值、返回值的不同类型对比&#xff0c;帮助大家一次性掌握两种代码编写能力。 目录 数据库 数据库表 实体类 对应的实体类如下&#x…

【LeetCode每日一题】——912.排序数组

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时间频度】九【代码实现】十【提交结果】 一【题目类别】 优先队列 二【题目难度】 中等 三【题目编号】 912.排序数组 四【题目描述】 给你一个整数…

H5 three.js 实现六年级观察物体

o(&#xffe3;▽&#xffe3;)ブ 我又带着新的demo来啦~ 预览 功能点 立方体的阴影 立方体的添加 位置记录 最大限制 三视图展示 立方体的移除 答题模式 随机出题 题库出题 源码 注释算是比较全了&#xff0c;可能部分会有点绕&#xff0c;还能够再优化一下~ <!DOCTYPE …

【第35章】Spring Cloud之Seata-Server快速入门

文章目录 前言一、准备1. 架构图2. 工作机制3. Seata术语4. 事务模式4.1 Seata AT 模式(依赖数据库)4.2 Seata TCC 模式(不依赖数据库)4.3 Seata Saga 模式(支持长事务)4.4 Seata XA 模式(支持XA 协议) 二、安装1. 下载2. 解压3. 重要属性4. 修改配置4.1 配置中心4.2 注册中心4…

C语言 13 指针

指针可以说是整个 C 语言中最难以理解的部分了。 什么是指针 还记得在前面谈到的通过函数交换两个变量的值吗&#xff1f; #include <stdio.h>void swap(int, int);int main() {int a 10, b 20;swap(a, b);printf("a %d, b %d", a, b); }void swap(int …

循环神经网络RNN+长短期记忆网络LSTM 学习记录

循环神经网络&#xff08;RNN) RNN的的基础单元是一个循环单元&#xff0c;前部序列的信息经处理后&#xff0c;作为输入信息传递到后部序列 x为输入向量&#xff0c;y为输出向量&#xff0c;a为上一隐藏层的a与x通过激活函数得到的值&#xff0c;简言之&#xff0c;每一层神…

华为 HCIP-Datacom H12-821 题库 (23)

&#x1f423;博客最下方微信公众号回复题库,领取题库和教学资源 &#x1f424;诚挚欢迎IT交流有兴趣的公众号回复交流群 &#x1f998;公众号会持续更新网络小知识&#x1f63c; 1.以下关于 VRRP 基本概念的描述&#xff0c;错误的是哪些选项&#xff1f; A、一个虚拟路由器…

S32K3 工具篇6:如何将RTD EB工程导入到S32DS

S32K3 工具篇6&#xff1a;如何将RTD EB工程导入到S32DS 1. MCAL_Plugins->Link Source Resource Filters2. Includes3. Preprocessor4. Linker5. optimization6. main.c 这个主题实际上&#xff0c;之前已经有多人写过&#xff0c;并且写的很好&#xff0c;只是实际操作中&…

qt-creator-10.0.2之后版本的jom.exe编译速度慢下来了

1、Qt的IDE一直在升级&#xff0c;qt-creator的新版本下载地址 https://download.qt.io/official_releases/qtcreator/ 2、本人一直用的是qt-creator-10.0.2版本&#xff0c;官网历史仓库可以下载安装包qt-creator-opensource-windows-x86_64-10.0.2.exe https://download.qt…

URP 线性空间 ui资源制作规范

前言&#xff1a; 关于颜色空间的介绍&#xff0c;可参阅 unity 文档 Color space URP实现了基于物理的渲染&#xff0c;为了保证光照计算的准确&#xff0c;需要使用线性空间&#xff1b; 使用线性空间会带来一个问题&#xff0c;ui资源在unity中进行透明度混合时&#xff…

COMP 6714-Info Retrieval and Web Search笔记week1

哭了哭了&#xff0c;这周唯一能听懂的就这门 目录 IR&#xff08;Information Retrieval)是什么&#xff1f;IR的基本假设Unstructured (text) vs. structuredDocuments vs. Database Records比较文本&#xff08;Comparing Text&#xff09;IR的范围(Dimensions of IR)IR的任…

YoloV10改进策略:上采样改进|动态上采样|轻量高效,即插即用(适用于分类、分割、检测等多种场景)

摘要 本文使用动态上采样改进YoloV10,动态上采样是今天最新的上采样改进方法,具有轻量高效的特点,经过验证,在多个场景上均有大幅度的涨点,而且改进方法简单,即插即用! 论文:《DySample:Learning to Upsample by Learning to Sample》 论文:https://arxiv.org/pdf/…

fmql之ubuntu移植

官方资料&#xff1a;ubuntu18的压缩包 目的&#xff1a;放到SD卡中启动ubuntu&#xff08;官方是放在emmc中&#xff09; 教程&#xff1a;99_FMQL45_大黄蜂开发板跑ubuntu18.04.docx 所需文件 其中&#xff0c;format_emmc_ext4.txt对emmc的分区是512M&#xff08;放上述文…

C++ | Leetcode C++题解之第397题整数替换

题目&#xff1a; 题解&#xff1a; class Solution { public:int integerReplacement(int n) {int ans 0;while (n ! 1) {if (n % 2 0) {ans;n / 2;}else if (n % 4 1) {ans 2;n / 2;}else {if (n 3) {ans 2;n 1;}else {ans 2;n n / 2 1;}}}return ans;} };

如何查看串口被哪个程序占用?截止目前最方便的方法

痛点&#xff1a;串口因为某种原因被占用&#xff0c;如何找到罪魁祸首&#xff1f; 做开发的小伙伴们&#xff0c;经常会遇到这样的问题&#xff1a;串口因为某种原因被占用&#xff0c;导致无法通讯&#xff0c;但是又找不到被哪个程序占用。只有重启电脑&#xff0c;才能解…