侧边栏壁纸
博主头像
小顺

一帆风顺 ⛵️⛵️⛵️

  • 累计撰写 70 篇文章
  • 累计创建 0 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

动手实现Tomcat

小顺
2022-08-31 / 0 评论 / 0 点赞 / 99 阅读 / 3,386 字

简单的实现一个Web容器

前提

  • Socket
  • IO

思路

  • 启动 Socket 服务,循环接收浏览器请求
  • 接收到请求之后,将流中的数据取出
  • 判断目标资源是否存在,若不存在,返回 404
  • 若存在,将目标资源通过输出流响应给客户端

代码

  1. Tomcat

    package com.apesblog.tomcat;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    /**
     * @author ZhangYuShun
     * @since 2022/8/31
     */
    public class Tomcat {
    
        /**
         * 一个简单的Web容器
         */
        public static void main(String[] args) throws IOException {
            //定义ServerSocket8080端口
            ServerSocket serverSocket = new ServerSocket(8080);
            //循环接受请求
            while (true) {
                Socket socket = serverSocket.accept();
                System.out.println(socket);
                new Thread(() -> {
                    try {
                        InputStream inputStream = socket.getInputStream();
                        Request request = new Request(inputStream);
                        //创建Response
                        OutputStream outputStream = socket.getOutputStream();
                        Response response = new Response(outputStream);
                        //进行响应
                        response.sendRedirect(request.getUrl());
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }).start();
            }
        }
    }
    
    
  2. Request

    package com.apesblog.tomcat;
    
    import java.io.IOException;
    import java.io.InputStream;
    
    /**
     * @author ZhangYuShun
     * @since 2022/8/31
     */
    public class Request {
        private String url;
        private String Method;
    
    
        public Request(InputStream inputStream) throws IOException {
            /**
             * 要一次读取多个字节时,经常用到InputStream.available()方法,这个方法可以在读写操作前先得知数据流里有多少个字节可以读取。需要注意的是,如果这个方法用在从本
             * 地文件读取数据时,一般不会遇到问题,但如果是用于网络操作,就经常会遇到一些麻烦。
             * 比如,Socket通讯时,对方明明发来了1000个字节,但是自己的程序调用available()方法却只得到900,或者100,甚至是0,感觉有点莫名其妙,怎么也找不到原因。
             * 其实,这是因为网络通讯往往是间断性的,一串字节往往分几批进行发送。本地程序调用available()方法有时得到0,
             * 这可能是对方还没有响应,也可能是对方已经响应了,但是数据还没有送达本地。对方发送了1000个字节给你,也许分成3批到达,
             * 这你就要调用3次available()方法才能将数据总数全部得到。
             */
            int count = 0;
            while (count == 0) {
                count = inputStream.available();
            }
            byte[] bytes = new byte[count];
            inputStream.read(bytes);
            String content = new String(bytes);
            System.out.println(content);
            if ("".equals(content)) {
                System.out.println("空请求");
            } else {
                //正则\s 转义\
                String firstLine = content.split("\\n")[0];
                this.setMethod(firstLine.split("\\s")[0]);
                this.setUrl(firstLine.split("\\s")[1]);
            }
        }
    
        public String getUrl() {
            return url;
        }
    
        public void setUrl(String url) {
            this.url = url;
        }
    
        public String getMethod() {
            return Method;
        }
    
        public void setMethod(String method) {
            Method = method;
        }
    }
    
  3. Response

    package com.apesblog.tomcat;
    
    import java.io.BufferedInputStream;
    import java.io.File;
    import java.io.OutputStream;
    import java.nio.file.Files;
    
    public class Response {
    
        private OutputStream outputStream;
    
        public Response(OutputStream outputStream) {
            this.outputStream = outputStream;
        }
    
        public void sendRedirect(String uri) {
            //判断uri是否存在
            //不存在返回404
            //存在直接返回目标资源数据
            File file = new File(Response.class.getResource("/").getPath() + uri);
            if (file.exists()) {
                try {
                    //返回目标资源数据
                    BufferedInputStream bufferedInputStream = new BufferedInputStream(Files.newInputStream(file.toPath()));
                    //存储每次读取的数据
                    byte[] bytes = new byte[1024];
                    //记录每次读取的有效字节个数
                    int len = 0;
                    //TODO 感觉写的不好 字节转字符再转字节输出   不够优雅
                    StringBuilder result = new StringBuilder();
                    while ((len = bufferedInputStream.read(bytes)) != -1) {
                        result.append(new String(bytes, 0, len));
                    }
                    String response = getResponseMessage("200", result.toString());
                    System.out.println(response);
                    this.outputStream.write(response.getBytes());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                try {
                    //返回404
                    String error = getResponseMessage("404", "404 File Not Found!");
                    this.outputStream.write(error.getBytes());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    
        public String getResponseMessage(String code, String message) {
            return "HTTP/1.1 " + code + "\r\n" + "Content-type: text/html\r\n" + "Content-Length: " + message.length() + "\r\n" + "\r\n" + message;
        }
    }
    
0

评论区