ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

自制简易Tomcat服务器

2021-12-26 14:02:11  阅读:140  来源: 互联网

标签:socket Tomcat 自制 public write 服务器 null os getBytes


在学习javaweb的时候我感觉自己是很懵逼的,因为好像就是写servlet然后配置web.xml,就可以让服务器提供服务了,对于这整个过程都不是很了解。为了弄懂整个的过程,我就找了黑马的自制简易Tomcat的视频,现将代码记录与这篇文章中。相信你看完整个过程,会对javaweb有更深的理解

模拟浏览器给服务器发送请求

// 模拟浏览器向服务器发送HTTP请求
public class TestClient {
    private static Socket socket = null;
    private static InputStream is = null;
    private static OutputStream os = null;

    public static void main(String[] args) throws Exception {
        //1_建立一个socket连接,连接itcast域名的80端口
        socket = new Socket("www.itcast.cn", 80);
        //2_获取到输出流对象
        is = socket.getInputStream();
        //3_获取到输入流对象
        os = socket.getOutputStream();
        //4_将HTTP协议的请求部分发送到服务端
        os.write("GET index.html HTTP/1.1\n".getBytes());
        os.write("HOST:www.itcast.cn\n".getBytes());
        os.write("\n".getBytes());
        //5_读取来自服务端的数据打印到控制台
        int i = is.read();
        while(i != -1){
            System.out.print((char)i);
            i = is.read();
        }
        //6_释放资源
        if(socket!=null){
            socket.close();
            socket = null;
        }
        if(is!=null){
            is.close();
            is = null;
        }
        if(os!=null){
            os.close();
            os = null;
        }
    }

}

模拟服务器响应客户端请求

public class TestServer {

    private static ServerSocket serverSocket = null;
    private static Socket socket = null;
    private static OutputStream os = null;

    public static void main(String[] args) throws IOException {
        try {
            //1_创建ServerSocket对象,监听本机的8080端口号
            serverSocket = new ServerSocket(8080);
            while(true) {
                //2_等待来自哭护短的请求获取和客户端对应的Socket对象
                socket = serverSocket.accept();
                //3_通过获取到的Socket对象获取到输出流对象
                os = socket.getOutputStream();
                //4_通过获取得到的输出流对象将HTTP协议的响应部分发送到客户端
                os.write("HTTP/1.1 200 OK\n".getBytes());
                os.write("Content-Type:text/html;charset=utf-8\n".getBytes());
                os.write("Server:Apache-Coyote/1.1\n".getBytes());
                os.write("\n\n".getBytes());
                StringBuffer buf = new StringBuffer();
                buf.append("<html>");
                buf.append("<head><title>我是标题</title></head>");
                buf.append("<body>");
                buf.append("<h1> I am header 1</h1>");
                buf.append("</body>");
                buf.append("</html>");
                os.write(buf.toString().getBytes());
                os.flush();
            }
        } catch (Exception e){
            e.printStackTrace();
        } finally {
            //5_释放资源
            if(serverSocket != null){
                serverSocket.close();
                serverSocket = null;
            }
            if(socket != null){
                socket.close();
                socket = null;
            }
            if(os != null){
                os.close();
                os = null;
            }
        }


    }

}

模拟服务器响应客户端对于静态资源的请求

我们要做的实际上就是读取客户端要访问的静态资源,然后将静态资源输出到客户端。

public class TestServer {

    // 定义一个变量,存放服务器WebContent的绝对路径
    public static String WEB_ROOT = System.getProperty("user.dir") + "\\" + "WebContent\\";
    // 定义静态变量,用于存放本次请求的静态页面名称
    private static String url = "";

    public static void main(String[] args) throws IOException {
        //System.out.println(WEB_ROOT);
        ServerSocket serverSocket = null;
        Socket socket = null;
        InputStream is = null;
        OutputStream os = null;
        // 创建一个ServerSocket,监听本机8080端口,等待客户端请求
        try {
            serverSocket = new ServerSocket(8080);
            while(true){
                socket = serverSocket.accept();
                is = socket.getInputStream();
                os = socket.getOutputStream();
                // 获取协议请求部分,截取客户端要访问的资源名称,将这个资源名称赋值给url
                parse(is);
                // 发送静态资源
                sendStaaticResource(os);
                socket.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 释放资源
            if(serverSocket != null){
                serverSocket.close();
                serverSocket = null;
            }
            if(socket != null){
                socket.close();
                socket = null;
            }
            if(is != null){
                is.close();
                is = null;
            }
            if(os != null){
                os.close();
                os = null;
            }
        }
    }
    // 获取协议请求部分,截取客户端要访问的资源名称,将这个资源名称赋值给url
    public static void parse(InputStream is) throws IOException {
        // 定义一个变量,存放HTTP协议请求部分数据
        StringBuffer content = new StringBuffer(2048);
        // 定义一个数组,存放HTTP协议请求部分数据
        byte[] buffer = new byte[2048];
        // 定义一个变量i,代表读取数据到数组中之后,数据量的带下
        int i;
        // 读取数据
        i = is.read(buffer);
        // 遍历字节数组,将数据追加到content变量中
        for(int j=0; j<i; j++){
            content.append((char)buffer[j]);
        }
        // 打印HTTP协议请求部分数据
        System.out.println(content);
        // 截取客户端请求的资源路径demo.html,赋值给url
        parseURL(content.toString());
    }

    // 截取客户端请求的资源路径demo.html,赋值给url
    private static void parseURL(String content) {
        int index1, index2;
        index1 = content.indexOf(" ");
        if(index1 != -1){
            index2 = content.indexOf(" ", index1+1);
            if(index2 > index1){
                url = content.substring(index1+2, index2);
            }
        }
        System.out.println(url);
    }

    // 发送静态资源
    public static void sendStaaticResource(OutputStream os) throws IOException {
        byte[] bytes = new byte[2048];
        FileInputStream fis = null;
        try{
            File file = new File(WEB_ROOT, url);
            if(file.exists()){
                os.write("HTTP/1.1 200 OK\n".getBytes());
                os.write("Server:apache-Coyote/1.1\n".getBytes());
                os.write("Content-Type:text/html;charset=utf-8\n".getBytes());
                os.write("\n".getBytes());
                fis = new FileInputStream(file);
                // 读取静态资源的内容到数组中
                int ch = fis.read(bytes);
                while(ch!=-1){
                    os.write(bytes, 0, ch);
                    ch = fis.read(bytes);
                }
            } else {
                os.write("HTTP/1.1 404 OK\n".getBytes());
                os.write("Server:apache-Coyote/1.1\n".getBytes());
                os.write("Content-Type:text/html;charset=utf-8\n".getBytes());
                os.write("\n".getBytes());
                String errorMessage="file not found";
                os.write(errorMessage.getBytes());
            }
        } catch (Exception e){
            e.printStackTrace();
        } finally {
            if(fis!=null){
                fis.close();
                fis = null;
            }
        }
    }
}

模拟在响应请求的过程中执行java程序

这个项目有点复杂。

我们最终实现的效果是,浏览器输入一个url。服务器解析完请求后,如果是动态请求就会通过反射调用相应的servlet提供服务;如果是静态请求就给客户端发送静态页面。

我们要写一个Servlet接口,所有的提供服务的java类都要实现这个接口

public interface Servlet {
    public void init();
    public void service(InputStream is, OutputStream os) throws IOException;
    public void destroy();
}

然后我们需要编写AAServlet.java和BBServlet.java

AAServlet.java

public class AAServlet implements Servlet{
    @Override
    public void init() {
        System.out.println("aaServlet...init");
    }

    @Override
    public void service(InputStream is, OutputStream os) throws IOException {
        System.out.println("aaServlet...service");
        os.write("I am from AAServlet!".getBytes());
        os.flush();
    }

    @Override
    public void destroy() {
        System.out.println("aaServlet...destroy");
    }
}

BBServlet.java

public class BBServlet implements Servlet{
    @Override
    public void init() {
        System.out.println("bbServlet...init");
    }

    @Override
    public void service(InputStream is, OutputStream os) throws IOException {
        System.out.println("bbServlet...service");
        os.write("I am from BBServlet!".getBytes());
        os.flush();
    }

    @Override
    public void destroy() {
        System.out.println("bbServlet...destroy");
    }
}

之后我们需要创建一个配置文件conf.properities,在里面填写映射关系

conf.properities

aa=TOMCAT2.AAServlet
bb=TOMCAT2.BBServlet

TestServer.java

public class TestServer {

    // 定义一个变量,存放服务器WebContent的绝对路径
    public static String WEB_ROOT = System.getProperty("user.dir") + "\\" + "WebContent\\";
    // 定义静态变量,用于存放本次请求的静态页面名称
    private static String url = "";
    // 定义一个静态的map,用来存储服务器中conf.properities中的信息
    private static HashMap<String, String> map  = new HashMap();

    static {
        Properties prop = new Properties();
        try {
            prop.load(new FileInputStream(WEB_ROOT + "//conf.properities"));
            Set set = prop.keySet();
            Iterator iterator = set.iterator();
            while(iterator.hasNext()){
                String key = (String)iterator.next();
                String value = prop.getProperty(key);
                map.put(key, value);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException, IllegalAccessException, InstantiationException, ClassNotFoundException {
        //System.out.println(WEB_ROOT);
        ServerSocket serverSocket = null;
        Socket socket = null;
        InputStream is = null;
        OutputStream os = null;
        // 创建一个ServerSocket,监听本机8080端口,等待客户端请求
        try {
            serverSocket = new ServerSocket(8080);
            while(true){
                socket = serverSocket.accept();
                is = socket.getInputStream();
                os = socket.getOutputStream();
                // 获取协议请求部分,截取客户端要访问的资源名称,将这个资源名称赋值给url
                parse(is);
                if(url!=null){
                    if(url.indexOf(".") != -1){
                        // 发送静态资源
                        sendStaaticResource(os);
                    } else {
                        // 发送动态资源
                        sendDynamicResource(is, os);
                    }
                }
                socket.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 释放资源
            if(serverSocket != null){
                serverSocket.close();
                serverSocket = null;
            }
            if(socket != null){
                socket.close();
                socket = null;
            }
            if(is != null){
                is.close();
                is = null;
            }
            if(os != null){
                os.close();
                os = null;
            }
        }
    }

    // 发送动态资源
    private static void sendDynamicResource(InputStream is, OutputStream os) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException {
        // 将HTTP协议的响应头和响应行发送给对方
        os.write("HTTP/1.1 OK\n".getBytes());
        os.write("Server:Apache\n".getBytes());
        os.write("Content-type:text/html;charset=utf-8\n".getBytes());
        os.write("\n".getBytes());
        // 判断map中是否存在一个key和本次待请求的资源路径一致
        if(map.containsKey(url)){
            // 如果包含指定的key,就可以获取到响应的value
            String value = map.get(url);
            // 通过反射将java程序加载到内存
            Class clazz = Class.forName(value);
            Servlet servlet = (Servlet)clazz.newInstance();
            // 执行init()方法
            servlet.init();
            // 执行service()方法
            servlet.service(is, os);
        }

    }

    // 获取协议请求部分,截取客户端要访问的资源名称,将这个资源名称赋值给url
    public static void parse(InputStream is) throws IOException {
        // 定义一个变量,存放HTTP协议请求部分数据
        StringBuffer content = new StringBuffer(2048);
        // 定义一个数组,存放HTTP协议请求部分数据
        byte[] buffer = new byte[2048];
        // 定义一个变量i,代表读取数据到数组中之后,数据量的带下
        int i;
        // 读取数据
        i = is.read(buffer);
        // 遍历字节数组,将数据追加到content变量中
        for(int j=0; j<i; j++){
            content.append((char)buffer[j]);
        }
        // 打印HTTP协议请求部分数据
        System.out.println(content);
        // 截取客户端请求的资源路径demo.html,赋值给url
        parseURL(content.toString());
    }

    // 截取客户端请求的资源路径demo.html,赋值给url
    private static void parseURL(String content) {
        int index1, index2;
        index1 = content.indexOf(" ");
        if(index1 != -1){
            index2 = content.indexOf(" ", index1+1);
            if(index2 > index1){
                url = content.substring(index1+2, index2);
            }
        }
        System.out.println(url);
    }

    // 发送静态资源
    public static void sendStaaticResource(OutputStream os) throws IOException {
        byte[] bytes = new byte[2048];
        FileInputStream fis = null;
        try{
            File file = new File(WEB_ROOT, url);
            if(file.exists()){
                os.write("HTTP/1.1 200 OK\n".getBytes());
                os.write("Server:apache-Coyote/1.1\n".getBytes());
                os.write("Content-Type:text/html;charset=utf-8\n".getBytes());
                os.write("\n".getBytes());
                fis = new FileInputStream(file);
                // 读取静态资源的内容到数组中
                int ch = fis.read(bytes);
                while(ch!=-1){
                    os.write(bytes, 0, ch);
                    ch = fis.read(bytes);
                }
            } else {
                os.write("HTTP/1.1 404 OK\n".getBytes());
                os.write("Server:apache-Coyote/1.1\n".getBytes());
                os.write("Content-Type:text/html;charset=utf-8\n".getBytes());
                os.write("\n".getBytes());
                String errorMessage="file not found";
                os.write(errorMessage.getBytes());
            }
        } catch (Exception e){
            e.printStackTrace();
        } finally {
            if(fis!=null){
                fis.close();
                fis = null;
            }
        }
    }
}

最后实现的效果如下

 

总结

这段程序我是跟着视频敲的,只是一个超级简化版的Tomcat,有许多不足的地方,但是对于理解Tomcat的运行原理是足够了。希望你能有所收获!

标签:socket,Tomcat,自制,public,write,服务器,null,os,getBytes
来源: https://blog.csdn.net/haohulala/article/details/122153318

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有