【学习笔记】手写 Tomcat 三

【学习笔记】手写 Tomcat 八 Non-Blocking I/O,非阻塞IO。我们之前使用的是 BIO 阻塞IO。 NIO 是同步非阻塞的,服务器的实现模式是一个线程处理多个连接。 因为获取的用户数据需要封装,需要一个类和数据库的字段一一对应,这就是 对象关系映射(ORM) 阅读详情

目录

一、多线程处理

1. 创建线程 thread

2. 使用 start() 启动线程

3. 启动服务端

二、响应动态资源

JDBC

创建 lib 文件夹,存放第三方 jar包 

JDBC 的7个步骤

定义响应动态资源的方法

DTO 数据传输对象

再创建一个响应方法

三、响应类的全部代码

四、测试

五、作业

1. 客户端发送 -1 报错怎么解决

2. JDBC 优化

六、预习

1. jq 的ajax

2. servlet


一、多线程处理

在昨天的基础上,再进一步优化,能够有一条线程专门处理一个客户端的请求

在使用多线程之前,我们先优化一下目前的结构

1. 创建线程 thread

package com.shao.net;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class Tomcat {
    public Tomcat() {
        ServerSocket ss = null;
        try {
            ss = new ServerSocket(8080);
            while (true) {

                // 调用accept()方法阻塞等待,直到有客户端连接到服务器,返回一个Socket对象用于与该客户端通信
                Socket socket = ss.accept();

                System.out.println("客户端连接成功");

                // 获取Socket对象的输入流,用于读取客户端发送的数据
                InputStream is = socket.getInputStream();

                // 获取Socket对象的输出流,用于向客户端发送数据
                OutputStream os = socket.getOutputStream();

                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        // 定义一个字节数组,存放客户端发送的请求信息
                        byte[] bytes = new byte[1024];

                        // 读取客户端发送的数据,返回读取的字节数
                        int len = 0;
                        try {
                            len = is.read(bytes);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }

                        // 将读取的字节数组转换为字符串
                        String msg = new String(bytes, 0, len);

                        // 调用HttpRequest类解析请求信息
                        HttpRequest httpRequest = new HttpRequest(msg);

                        // 拼接请求的静态资源的路径
                        // 路径是相对路径,从模块的根路径开始
                        String filePath = "webs/pages" + httpRequest.getRequestModule();
                        HttpResponse httpResponse = new HttpResponse(os, httpRequest);
                        // 响应数据
                        httpResponse.response(filePath);
                    }
                }).start(); // 启动线程
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭连接通道
            try {
                ss.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

2. 使用 start() 启动线程

new Thread(new Runnable() {
    @Override
    public void run() {
    }
}).start();

3. 启动服务端

在启动类的 main 方法中 创建 Tomcat 对象启动服务端


 

二、响应动态资源

现在已经可以响应静态资源了,如何响应动态资源呢?

JDBC

动态资源一般需要操作数据库,所以我们要使用 java 数据库连接  也就是JDBC 

使用JDBC 操作数据库,需要使用到第三方 jar 包

因为java 提供了统一的接口,不同的数据库厂商实现这个接口,所以使用统一的语法就可以操作不同的数据库

在选择第三方 jar 包的时候需要根据所使用的数据库去下载

因为我使用的数据库是 Mysql ,所以 jar 包是从Mysql官网下载的

百度网盘 mysql jar包

创建 lib 文件夹,存放第三方 jar包 

JDBC 的7个步骤

1. 加载JDBC 驱动

2. 定义连接数据库的URL

3. 建立数据库连接

4. 创建可执行对象 statement

5. 执行SQL

6. 结果处理

7. 释放连接

定义响应动态资源的方法

响应静态资源 index.html 的时候是根据扩展名 .html 判断的

响应动态资源不需要写文件名了,只需要写功能的名称即可

比如登录功能,可以定义名称为 doLogin

1. 加载JDBC 驱动

2. 定义连接数据库的URL

3. 建立数据库连接

4. 创建可执行对象 statement

5. 执行SQL

6. 结果处理

这是我的数据库的 user 表

7. 释放资源

因为变量都定义在 try 语句块里,是局部变量,finally 语句块无法获取到,所以需要抽取出来

    private static void releaseSource(Connection connection, PreparedStatement pstmt, ResultSet resultSet) {

        try {
            if (connection != null) {
                connection.close();
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }

        try {
            if (pstmt != null) {
                pstmt.close();
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }

        try {
            if (resultSet != null) {
                resultSet.close();
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

DTO 数据传输对象

DTO 是一种服务端给客户端传输数据的格式

package com.shao.Utils;

public class responseDTO {

    private int statusCode;
    private Object data;
    private String msg;
    private int count;


    public responseDTO() {
    }

    public responseDTO(int statusCode, Object data, String msg) {
        this.statusCode = statusCode;
        this.data = data;
        this.msg = msg;
    }

    public responseDTO(int statusCode, Object data, String msg, int count) {
        this.statusCode = statusCode;
        this.data = data;
        this.msg = msg;
        this.count = count;
    }

    /**
     * 获取
     *
     * @return statusCode
     */
    public int getStatusCode() {
        return statusCode;
    }

    /**
     * 设置
     *
     * @param statusCode
     */
    public void setStatusCode(int statusCode) {
        this.statusCode = statusCode;
    }

    /**
     * 获取
     *
     * @return data
     */
    public Object getData() {
        return data;
    }

    /**
     * 设置
     *
     * @param data
     */
    public void setData(Object data) {
        this.data = data;
    }

    /**
     * 获取
     *
     * @return msg
     */
    public String getMsg() {
        return msg;
    }

    /**
     * 设置
     *
     * @param msg
     */
    public void setMsg(String msg) {
        this.msg = msg;
    }

    /**
     * 获取
     *
     * @return count
     */
    public int getCount() {
        return count;
    }

    /**
     * 设置
     *
     * @param count
     */
    public void setCount(int count) {
        this.count = count;
    }

    public String toString() {
        return "responseDTO{statusCode = " + statusCode + ", data = " + data + ", msg = " + msg + ", count = " + count + "}";
    }
}

再创建一个响应方法

在 httpResponse 响应类中 再创建一个响应方法,响应动态资源

    public void send(String content) {
        // 转成字节数组
        byte[] contentBytes = content.getBytes();
        // 获取请求协议
        String protocol = httpRequest.getRequestProtocol();
        try {
            os.write((protocol + " 200 OK\r\n").getBytes());
            os.write(("Content-Type: " + "text/html;charset=utf-8" + "\r\n").getBytes());
            os.write(("Content-Length: " + contentBytes.length + "\r\n").getBytes());
            os.write("\r\n".getBytes());
            os.write(contentBytes);
            os.flush();
            System.out.println("响应成功");
            os.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

三、响应类的全部代码

package com.shao.net;

import com.shao.Utils.responseDTO;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.sql.*;

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("请求动态资源");
            if (httpRequest.getRequestModule().equals("/doLogin")) {

                Connection connection = null;
                PreparedStatement pstmt = null;
                ResultSet resultSet = null;

                // 1. 加载类驱动器
                try {
                    Class<?> aClass = Class.forName("com.mysql.cj.jdbc.Driver");
                    // 2.定义数据库连接的URL
                    String url = "jdbc:mysql://localhost:3306/train";
                    String user = "root";
                    String password = "sql123";

                    // 3. 获取数据库连接
                    connection = DriverManager.getConnection(url, user, password);

                    // 4. 获取可执行对象
                    String SQL = "select count(*) from train.users where account = ? and password = ?";
                    pstmt = connection.prepareStatement(SQL);

                    // 设置占位符的值
                    String account = httpRequest.getRequestBodyParams().get("account");
                    String pwd = httpRequest.getRequestBodyParams().get("password");

                    pstmt.setString(1, account);
                    pstmt.setString(2, pwd);

                    // 5. 执行sql语句,获取结果集
                    resultSet = pstmt.executeQuery();

                    // 6. 结果处理
                    responseDTO responseDTO = null;
                    if (resultSet.next() && resultSet.getInt(1) > 0) {
                        responseDTO = new responseDTO(200, null, "登录成功");
                    } else {
                        responseDTO = new responseDTO(201, null, "登录失败,请检查账号和密码");
                    }

                    //调用方法返回数据
                    send(responseDTO.toString());

                } catch (Exception e) {
                    e.printStackTrace();
                } finally {

                    // 7. 释放资源
                    releaseSource(connection, pstmt, resultSet);
                }
            }
        }
    }

    /**
     * 释放资源
     */
    private static void releaseSource(Connection connection, PreparedStatement pstmt, ResultSet resultSet) {

        try {
            if (connection != null) {
                connection.close();
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }

        try {
            if (pstmt != null) {
                pstmt.close();
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }

        try {
            if (resultSet != null) {
                resultSet.close();
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 响应静态资源
     */
    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();
        }
    }

    public void send(String content) {
        // 转成字节数组
        byte[] contentBytes = content.getBytes();
        // 获取请求协议
        String protocol = httpRequest.getRequestProtocol();
        try {
            os.write((protocol + " 200 OK\r\n").getBytes());
            os.write(("Content-Type: " + "text/html;charset=utf-8" + "\r\n").getBytes());
            os.write(("Content-Length: " + contentBytes.length + "\r\n").getBytes());
            os.write("\r\n".getBytes());
            os.write(contentBytes);
            os.flush();
            System.out.println("响应成功");
            os.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

四、测试

在游览器的地址栏 输入 http://127.0.0.1:8080/doLogin?account=16701&password=Abc123456

http://127.0.0.1:8080 是本地地址

/doLogin 是功能的名称

account=16701&password=Abc123456 是请求的参数

可以看到请求的参数信息

五、作业

1. 客户端发送 -1 报错怎么解决

2. JDBC 优化

六、预习

1. jq 的ajax

2. servlet

智警杯1.4---excel可视化 知识点 阅读详情

相关推荐

CST周期边界仿真:揭秘频率选择表面(FSS)单元设计全流程

本文详细解析了CST周期边界仿真在频率选择表面(FSS)单元设计中的应用全流程。从理论基础到工程实践,涵盖了求解器选择、几何建模、边界设置、端口优化等关键步骤,并提供了常见问题排查指南。通过CST仿真技术,工程师能够高效预测FSS的电磁特性,为实际应用提供可靠的设计依据。

weixin_30571465的博客 357

手写Tomcat

tomcat提供的服务: 第一:提供Socket服务 第二:进行请求的分发 第三:需要把请求和响应封装成request/response

书生的博客 4098

AI报告撰写实战指南:从提示词工程到全流程优化的底层逻辑与实践突破

传统AI写作常被诟病为“信息拼贴”——模型虽能生成文本,却常出现逻辑断裂、数据矛盾、风格不符等问题。而“全流程优化”则是通过系统化设计,将报告生成拆解为需求解析→数据对齐→提示词设计→初稿生成→迭代优化→格式适配六大环节,每个环节均通过技术手段提升输出质量。提示词工程:本质是通过结构化指令(如角色设定、任务分解、格式约束、示例引导)精准控制模型输出方向,是连接用户需求与模型能力的“翻译器”。全流程优化。

2501_92808811的博客 884

自己手写tomcat项目

在Servlet(接口中)有:1.init():初始化servlet2.getServletConfig():获取当前servlet的配置信息3.service():服务器(在HttpServlet中实现,目的是为了更好的匹配http的请求方式)4.getServletInfo():获取servlet当前运行过程中的信息5.destroy():销毁,回收内存在I/O中应包含:1.请求头(f12+网络) 2.请求方式(get/post) 3.请求内容。

2302_79585857的博客 1799

异步FIFO的隐形陷阱:复位信号同步化实战解析

本文深入解析异步FIFO设计中复位信号同步化的隐形陷阱,重点分析赛灵思xpm_fifo_async原语在FPGA应用中的常见问题。通过实战代码和系统集成方案,提供复位同步化的最佳实践,帮助工程师避免跨时钟域通信故障,提升系统可靠性。

keras9composer的博客 244

【学习笔记】计算机网络 第八章 无线局域网(WLAN)

计算机网络 第八章 无线局域网(WLAN)

Vera_shsf的博客 1240

【学习笔记】手写 Tomcat -- 预备知识

I 是 Input,O 是Output,也就是输入输出流。当我们操作数据时,以内存为基准,把数据读取到内存中是输入,把数据从内存写入到硬盘是输出。

LearnTech_123的博客 1644

OpenV2X 车路协同开源平台 Columbia 版本正式发布!

OpenV2X车路协同开源平台Columbia 版本正式发布!

weixin_65571684的博客 1198

【学习笔记】手写 Tomcat 六

昨天使用了数据库连接池,我们了解了连接池的优点,那么也可以使用线程池来管理线程,java自带的线程池的参数有 核心线程数,最大线程数,线程活跃时间,时间单位,任务队列,线程工厂,拒绝策略。为了学习了解线程池,我们先手写一个简单的线程池,只需要做到核心线程可重复利用就行。

LearnTech_123的博客 1763

GD32F130之USART多机通信

简介 在多机通信中,一个从机应该只接收它需要接收的数据,且只应该在它需要接收数据时才检测串口。而始终接收RX线上的数据对从机来说是一种负担。 多机通信硬件设计 远距离方案:RS485 板级方案: 静默模式 静默模式有什么作用? 答:如果 USART 处于静默模式,所有的接收状态标志位将不会被置位。 如何进入静默模式? 答:软件可以通过将 USART_CMD 寄存器中MMCMD 位置 1 使 USART 进入静默模式。 如何从静默模式中唤醒? 答:有两种方式可选,①检测到总线

luliplus的博客 299

【网络安全 | 渗透工具】Githack工具安装及使用教程详析

本文仅分享Githack工具基本安装及使用相关知识不承担任何法律责任。Git是一个非常流行的开源分布式版本控制系统,它被广泛用于协同开发和代码管理。许多网站和应用程序都使用Git作为其代码管理系统,并将其部署到生产环境中以维护其代码库。然而,在配置不当的情况下,可能会导致.git文件夹被直接部署到线上环境中,这可能会导致Git泄露问题。可通过githack下载泄露的Git存储库,以获取包含未加密密码、凭据和敏感信息的站点代码库。使用githack下载Git存储库的方法有两种。本文仅分享。

等风来 3万+

手写简易Tomcat

通过手写tomcat对java中的网络通信有了更进一步的了解

Java新生代 1411

python实现DoIP基本通信(收发报文)

python实现DoIP基本通信(收发报文)

天赐好车的博客 895

18_1手写Tomcat学习笔记

tomcat学习笔记 文章目录1 tomcat简介1.1 web服务器1.2 tomcat服务器1.2.1 下载1.2.2 安装1.2.3 配置1.2.4 tomcat目录结构1.2.5 如何去访问一个web应用的某个文件1.2.6 在web服务器中安装web应用1.2.7 配置虚拟目录1.2.8 虚拟主机1.3 浏览器访问web站点原理2 手写tomcat_静态资源3 手写tomcat_动态资源3.1 tomcat工作原理3.2 http get请求格式3.2 http post请...

tangguanlin2008的博客 3741

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

【学习笔记】手写 Tomcat 二

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

LearnTech_123的博客 1485

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

手写一个Tomcat

Tomcat 是一个广泛使用的开源 Java Servlet 容器,用于运行 Java Web 应用程序。虽然 Tomcat 本身功能强大且复杂,但通过手写一个简易版的 Tomcat,我们可以更好地理解其核心工作原理。本文将带你一步步实现一个简易版的 Tomcat,并深入探讨其核心组件和运行机制。

2301_77071977的博客 2037

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

【学习笔记】手写 Tomcat 五

我们把功能都写在了 Servlet,这样有几个缺点,一是不够低耦合 高内聚,二是不方便协同开发等。我们可以使用三层架构来解决这个问题。三层架构区分层次的目的是为了 “高内聚,低耦合”。

LearnTech_123的博客 1184

从零开始手写Tomcat,一文彻底搞懂Tomcat运行流程(附源码)

一:理论铺垫 tomcat的功能是什么? tomcat是java的一个中间件,浏览器发出HTTP请求后经过tomcat中间件,转发到目的服务器,目的服务器返回响应消息,通过tomcat返回给浏览器。tomcat的使用很简单,但是作为合格的程序员,光会用可不行,接下来就通过手写一个tomcat彻底搞懂tomcat。 在手写Tomcat之前,首先重温一下http和servlet,http协议...

Java鱼仔的博客 7764

如何用JAVA手写一个Tomcat

Socket(套接字)是计算机网络中用于进程间通信的核心技术,它允许不同主机或同一主机上的不同程序通过网络交换数据Socket 是什么?定义:Socket 是操作系统提供的抽象接口,本质上是通信的端点(Endpoint)。它封装了底层网络协议(如TCP/IP或UDP)的复杂性,提供了一组简单的API【API(Application Programming Interface,应用程序编程接口)是软件系统之间交互的桥梁,定义了如何请求服务、传递数据以及返回结果的规则。】供应用程序使用。关键要素IP地址。

2301_80391652的博客 2580

手写Tomcat框架

模型核心逻辑并发能力复杂度BIO一请求一线程,全程阻塞低简单NIO线程 + Selector 管多连接中高中等AIO异步通知,线程不等待高复杂2.扫描包的类用的是一个工具类进行实现的2.1首先 进行遍历获取类信息2.2通过反射机制,从目标类(clazz)中提取它上面标注的@WebServlet注解对象2.3这里获取类的无参构造方法 ---创建新的对象并强转向上转型为。

2302_80192044的博客 978

Tomcat原理之手写

目录 一、准备工作 1.1 Tomcat概要 1.2 技术储备 二、实现步骤 三、相关类的实现和介绍 3.1 包结构 3.2 Servlet配置类 3.3 Tomcat实现类 3.4 请求响应实现类Request/Response 3.5 工具类HttpUtil/FileUtil 3.6 Servlet抽象和测试实现类 四、测试运行 4.1 成功请求servlet(200...

天天向上 1047

手写一个Tomcat服务器

作为java开发者,都知道Tomcat是一个家户喻晓的Web服务器。手写一个Tomcat对我们理解Tomcat的工作原理,还有servlet的执行流程有很大的帮助,下面是我手写一个简要版Tomcat的总结和收获! 首先,先还原Tomcat的本质: 相信大家之前在开发的时候,虽然已经能上手开发web程序,但是对于Servlet的执行流程以及项目还会存在很多疑问,下面是我总结的我之前的疑问也给出相应...

yechengchao的博客 2051

如何基于Netty手写简单的Tomcat?

Spring MVC 处理业务逻辑的,我们感知不到它的存在,平常我们只需要写Controller ,这是因为DispatcherServlet拦截了所有的请求。SpringBoot 提倡零配置,使用注解方式是Spring MVC 的主流。Spring 容器启动时会扫描相关类,并且j解析类上、方法上注解。类上URL作为基础路径和方法上URL拼接作为最终URL ,而对应方法即最终URL的处理器。相关的映射关系也被称为handler mappings。

happycao123的博客 1063
上一篇: 【学习笔记】手写 Tomcat 二
下一篇: 【学习笔记】手写 Tomcat 四
LearnTech_123
博客等级 码龄3年 445粉丝 31原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值