JavaWeb,Tomcat基本思想,手写Tomcat

DAY14.1 Java核心基础

JavaWeb

javaWeb 是指的是java提供了一系列组件来开发web应用程序,基于java网络编程

Socket、Servlet、Filter底层都是网络编程,进行了封装,我们只需要调用相关方法使用即可

Tomcat介绍

Tomcat是一个web容器,开发好的程序放入Tomcat里面就可以直接运行了,客户端就可以访问

单独的程序是无法直接运行的,必须依赖web容器

Web容器以内的资源可以允许外部直接访问,Web容器以外的资源无法直接访问

常见的Web容器:Tomcat、Jboss、Weblogic

Tomcat安装之后的包的作用:

bin:各种脚本,比如启动和关闭Tomcat

conf:配置文件,可以设置端口等...信息

lib:相关的jar包,比如servlet...

logs:运行日志文件

temp:临时文件

webapps:需要运行的程序,资源

work:JSP转换之后的servlet文件

实际开发中不需要在这启动,在开发的时候可以导入到IDEA里面

创建Web项目:

编辑配置,找到相应的tomcat配置

根据路径访问主路径下面的img.png图片呢,可以访问到

需要注意

应用程序上下文可以删除,这样就直接可以在端口后面加路径了,http://localhost:3030/img.png

WEB-INF里面的文件无法通过浏览器路径访问

导入Servlet依赖

xml 复制代码
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>4.0.1</version>
  <scope>provided</scope>
</dependency>

创建一个测试接口

java 复制代码
@WebServlet("/test")
public class Test extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setCharacterEncoding("GBK");
        resp.getWriter().write("Hello World,这是Tomcat!");
    }
}

添加工件:

需要注意我这里将这个上下文给取消了,这样访问的时候就没有相应工件的代码了

只需要在端口后面加 /路径 即可

启动tomcat

浏览器访问

可以看见访问到接口test的内容了

重点

手写一个Tomcat容器

java网络编程、Socket

要求:手写一个 Web 应用服务器,不能使用 Servlet,要求使用 Socket 来编程

  • 可以响应 Get 请求
  • 打印请求信息
  • 判断请求的资源是否存在,若不存在,给出 404 错误提示
  • 若资源存在,返回该资源
  • 并且有默认的资源页面
  • 要求客户端使用浏览器进行访问

思路:

  • 需要有一个对象来持续接收端口的请求
  • 还需要有一个对象来封装以及处理接收到的请求
  • 还有一个对象来封装返回的结果

创建一个HttpRequest对象来接收接收到的请求

关键点:

  • 通过服务器accept ()接收到的socket对象获取到inputStream传入对象
  • 处理inputSteam,获取到访问路径的uri,存入到该对象的uri中
  • inputSteam读取到的数据为

可以通过读取的第一个和第二个空格取出访问的路径/index.html

java 复制代码
//处理请求
int index1,index2;
index1= buffer.indexOf(" ");
index2 = buffer.indexOf(" ",index1+1);
uri = buffer.substring(index1+1,index2);
HttpRequest代码:
java 复制代码
public class HttpRequest {
    private InputStream inputStream;
    private String uri;

    public String getUri() {
        return uri;
    }

    public HttpRequest(InputStream inputStream) {
        this.inputStream = inputStream;
    }

    /**
     * 获取请求的路径文件
     * @return
     */
    public String parse() {
        StringBuffer buffer = new StringBuffer(2048);
        // 处理请求
        int lenth =0;
        byte[] bytes = new byte[2048];
        try {
            lenth = inputStream.read(bytes);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        for (int j = 0; j < lenth; j++) {
            // 读取请求
            buffer.append((char) bytes[j]);
        }
        //处理请求
        int index1,index2;
        index1= buffer.indexOf(" ");
        index2 = buffer.indexOf(" ",index1+1);
        uri = buffer.substring(index1+1,index2);

        System.out.println("uri:" + uri);
        // 打印请求
        System.out.println(buffer);
        return uri;
    }
}

创建一个对象用于封装返回的数据资源

关键点:

  • 通过传入ServerSocket的accept()方法获取到OutputStream来创建一个HttpResponse对象
  • data和code是封装状态码和静态资源
  • sendStaticResource()方法可以发送静态资源
  • 根据路径ServerSocket的WebContent封装的默认静态资源目录加上路径后缀得到相关的资源路径
  • 然后用传入的InputStream来读取静态资源文件,封装到data中
  • 用StringBuilder封装返回的内容,需要返回浏览器可以读取的形式
java 复制代码
StringBuilder response = new StringBuilder();
response.append("HTTP/1.1 ").append(code).append(" Not OK\r\n");
response.append("Content-Type: text/html; charset=UTF-8\r\n");
// 修正 Content-Length 为字节长度
response.append("Content-Length: ").append(data.getBytes(StandardCharsets.UTF_8).length).append("\r\n");
response.append("\r\n");
response.append(data);
  • 通过OutputStream输出到浏览器
java 复制代码
this.outputStream.write(response.toString().getBytes(StandardCharsets.UTF_8));
  • 访问路径为空和"/"的情况做一个处理转入到默认静态资源路径
  • 如果找不到则返回一个设置好的404.html界面
HttpResponse代码:
java 复制代码
public class HttpResponse {
    private final OutputStream outputStream;
    private String data;
    private Integer code;

    public HttpResponse(OutputStream outputStream, String data, Integer code) {
        this.outputStream = outputStream;
        this.data = data;
        this.code = code;
    }

    public void sendStaticResource(HttpRequest httpRequest) throws UnsupportedEncodingException {
        // 判断当前的请求资源,如果为/或者空,则默认设置到首页
        String uri = httpRequest.getUri();
        if ("/".equals(uri) || uri.isEmpty()) {
            uri = "/index.html";
            System.out.println("默认页面测试............");
        }
        // 判断文件资源是否存在,如果存在返回响应,如果不存在返回404响应
        File file = new File(MyHttpServer.WebContent + uri);
        byte[] bytes = new byte[(int) file.length()];
        if (file.exists() && file.isFile()) {
            // 读取文件内容
            try {
                InputStream inputStream = Files.newInputStream(file.toPath());
                inputStream.read(bytes);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            data = new String(bytes, StandardCharsets.UTF_8);
            code = 200;
        } else {
            code = 404;
            // 读取预定义的404.html文件内容
            File errorFile = new File(MyHttpServer.WebContent + "/404.html");
            if (errorFile.exists()) {
                try (InputStream inputStream = Files.newInputStream(errorFile.toPath())) {
                    byte[] errorBytes = new byte[(int) errorFile.length()];
                    inputStream.read(errorBytes);
                    data = new String(errorBytes, StandardCharsets.UTF_8);
                } catch (IOException e) {
                    // 回退到默认文本
                    data = "404 Not Found";
                }
            } else {
                // 若404.html不存在则使用默认文本
                data = "404 Not Found";
            }
        }
        StringBuilder response = new StringBuilder();
        response.append("HTTP/1.1 ").append(code).append(" Not Found\r\n");
        response.append("Content-Type: text/html; charset=UTF-8\r\n");
        // 修正 Content-Length 为字节长度
        response.append("Content-Length: ").append(data.getBytes(StandardCharsets.UTF_8).length).append("\r\n");
        response.append("\r\n");
        response.append(data);

        try {
            this.outputStream.write(response.toString().getBytes(StandardCharsets.UTF_8));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}

创建一个用于接收请求的ServerSocket服务器

关键点:

  • 通过创建对象的时候传递进来这个端口和IP,用于创建一个ServerSocket对象
  • 循环接收socket = serverSocket.accept();
  • 接收到了获取一个输入流,将输出流创建一个HttpRequest对象进行处理,得到访问路径uri
  • 然后格局socket获取到输出流,通过创建一个HttpResponse对象进行处理,将静态资源返回到浏览器上面
MyHttpServer代码:
java 复制代码
public class MyHttpServer {
    private InetSocketAddress inetSocketAddress;
    public static String WebContent =System.getProperty("user.dir")+ File.separator+"Webcontent";

    public MyHttpServer(InetSocketAddress inetSocketAddress) {
        this.inetSocketAddress = inetSocketAddress;
    }

    public void start() {
        // 创建一个ServerSocket来接收请求
        InputStream inputStream = null;
        OutputStream outputStream =null;
        Socket socket = null;
        try {
            ServerSocket serverSocket = new ServerSocket(inetSocketAddress.getPort(), 1, inetSocketAddress.getAddress());
            while (true){
                System.out.println("等待接收客户端请求...");
                socket = serverSocket.accept();
                // 将这个输入流传递给HttpRequest,处理请求
                inputStream = socket.getInputStream();
                HttpRequest httpRequest = new HttpRequest(inputStream);
                httpRequest.parse();


                // 将这个输出流传递给HttpResponse,处理响应
                outputStream = socket.getOutputStream();
                HttpResponse httpResponse = new HttpResponse(outputStream, "<h1>Hello World,这个是返回结果</h1>", 404);
                httpResponse.sendStaticResource(httpRequest);
                System.out.println("请求处理完毕");
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            try {
                inputStream.close();
                outputStream.close();
                socket.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
相关推荐
程序员鱼皮20 分钟前
2025 年最全Java面试题 ,热门高频200 题+答案汇总!
java·后端·面试
爱笑的Sunday23 分钟前
【LeetCode 题解】算法:15.三数之和
java·数据结构·算法·leetcode
爱编程的王小美28 分钟前
srpingboot-后端登录注册功能的实现
java·数据库·sql
该怎么办呢34 分钟前
原生android实现定位java实现
android·java·gitee
没差c43 分钟前
处理json,将接口返回的数据转成list<T>,和几个时间处理方法的工具类
java·json·list
Hanson Huang44 分钟前
23中设计模式-迭代器(Iterator)设计模式
java·设计模式·迭代器模式·行为型设计模式
天草二十六_简村人1 小时前
Rabbitmq消息被消费时抛异常,进入Unacked 状态,进而导致消费者不断尝试消费(下)
java·spring boot·分布式·后端·rabbitmq
低头不见1 小时前
Spring Boot 的启动流程
java·spring boot·后端
小码农<^_^>1 小时前
linux环境变量
java·linux·运维
计算机-秋大田2 小时前
基于Spring Boot的消防物资存储系统的设计与实现(LW+源码+讲解)
java·vue.js·spring boot·后端·课程设计