自学内容网 自学内容网

【学习笔记】手写 Tomcat 三

目录

一、多线程处理

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


原文地址:https://blog.csdn.net/LearnTech_123/article/details/142179075

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!