TomCat

静态web资源服务器

把本地上面的资源共享出来,使用socket

版本一:校验文件并输出

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @ClassName MainServer
 * @Description:
 *  1.启动一个程序,持续去监听某一端口号
 *  2.获取客户端提交过来的信息(如果使用浏览器来发送,HTTP请求报文)
 *  3.解析请求资源
 *  4.尝试在服务器硬盘上面去查找该文件,如果找到,则写入到响应体中;如果没有找到,则写入404
 * @Author 远志 zhangsong@cskaoyan.onaliyun.com
 * @Date 2021/12/6 11:51
 * @Version V1.0
 **/
public class MainServer {

    public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(8080);
            while (true){
                //其实就是java语言中对于tcp连接的封装
                Socket client = serverSocket.accept();
                //客户端发送过来的全部都是文本数据
                //SocketInputStream读取的时候,读取到末尾会阻塞住,不会立刻返回-1,一段时间没有读取到,才会最终返回-1
                InputStream inputStream = client.getInputStream();
                byte[] bytes = new byte[1024];
                int length = inputStream.read(bytes);
                String content = new String(bytes, 0, length);
                System.out.println(content);
            }

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

    }
}

版本二:解析请求报文并封装进一个request对象

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @ClassName MainServer
 * @Description:
 *  1.启动一个程序,持续去监听某一端口号
 *  2.获取客户端提交过来的信息(如果使用浏览器来发送,HTTP请求报文)
 *  3.解析请求资源
 *  4.尝试在服务器硬盘上面去查找该文件,如果找到,则写入到响应体中;如果没有找到,则写入404
 * @Author 远志 zhangsong@cskaoyan.onaliyun.com
 * @Date 2021/12/6 11:51
 * @Version V1.0
 **/
public class MainServer {

    public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(8080);
            while (true){
                //其实就是java语言中对于tcp连接的封装
                Socket client = serverSocket.accept();
                //客户端发送过来的全部都是文本数据
                //SocketInputStream读取的时候,读取到末尾会阻塞住,不会立刻返回-1,一段时间没有读取到,才会最终返回-1
                Request request = new Request(client);
                //request.getHeaderNames()
            }

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

    }
}
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @ClassName Request
 * @Description: 把请求报文封装到request对象中
 * @Author 远志 zhangsong@cskaoyan.onaliyun.com
 * @Date 2021/12/6 14:43
 * @Version V1.0
 **/
public class Request {

    private String requestString;

    /**
     * 请求方法
     */
    private String method;

    /**
     * 请求资源
     */
    private String requestURI;

    /**
     * 版本协议
     */
    private String protocol;

    private Map<String, String> requestHeaders = new HashMap<>();

    public Request(Socket client) {
        try {
            InputStream inputStream = client.getInputStream();
            byte[] bytes = new byte[1024];
            int length = inputStream.read(bytes);
            this.requestString = new String(bytes, 0, length);
            parseRequestLine();
            parseRequestHeaders();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     *
     *
     * @title:
     * @createAuthor: 远志
     * @createDate: 2021/12/6 14:55
     * @description: 解析请求头
     * @version: 1.0
     * @return:
     */
    private void parseRequestHeaders() {
        int begin = requestString.indexOf("\r\n");
        int end = requestString.indexOf("\r\n\r\n");
        String substring = requestString.substring(begin + 2, end);
        String[] parts = substring.split("\r\n");
        for (String part : parts) {
            int i = part.indexOf(":");
            String headerName = part.substring(0, i).trim();
            String headerValue = part.substring(i + 1).trim();
            requestHeaders.put(headerName, headerValue);
        }
    }

    /**
     *
     *
     * @title:
     * @createAuthor: 远志
     * @createDate: 2021/12/6 14:46
     * @description:
     * 解析请求行
     * 将请求报文进行拆解,拆分成若干部分
     * 利用换行符来进行拆分\r\n
     * 0-\r\n  请求行
     * \r\n-----\r\n\r\n  请求头
     * @version: 1.0
     * @return:
     */
    private void parseRequestLine() {
        //拿到第一次出现\r\n的下标位置
        int index = requestString.indexOf("\r\n");
        String requestLine = requestString.substring(0, index);
        String[] parts = requestLine.split(" ");
        this.method = parts[0];
        //如果浏览器是以get请求方法发送请求,同时携带了请求参数,那么请求参数会附着在uri中
        //此时如果不把参数去掉,则找不到该文件
        this.requestURI = parts[1];
        this.protocol = parts[2];
        //主要用来判断请求资源中是否有请求参数
        //正常情况下,我们还需要进一步去处理请求参数,我们这里面为了简便,就不去处理了
        int i = requestURI.indexOf("?");
        if(i != -1){
            requestURI = requestURI.substring(0, i);
        }
    }

    public String getMethod() {
        return method;
    }

    public String getRequestURI() {
        return requestURI;
    }

    public String getProtocol() {
        return protocol;
    }

    public String getHeader(String headerName){
        return requestHeaders.get(headerName);
    }

    public Set<String> getHeaderNames(){
        return requestHeaders.keySet();
    }
}

此时存在一个问题,socketInputstream在读取完毕后不会立刻返回-1,会阻塞一段时间再返回-1,这里要注意使用监听端口获得的inputstream并不是我们常用的FileInputStream,所以才会有这种问题,于是需要采用多线程来解决

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

/**
 * @ClassName MainServer
 * @Description:
 *  1.启动一个程序,持续去监听某一端口号
 *  2.获取客户端提交过来的信息(如果使用浏览器来发送,HTTP请求报文)
 *  3.解析请求资源
 *  4.尝试在服务器硬盘上面去查找该文件,如果找到,则写入到响应体中;如果没有找到,则写入404
 * @Author 远志 zhangsong@cskaoyan.onaliyun.com
 * @Date 2021/12/6 11:51
 * @Version V1.0
 **/
public class MainServer {

    public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(8080);
            while (true){
                //其实就是java语言中对于tcp连接的封装
                Socket client = serverSocket.accept();
                //客户端发送过来的全部都是文本数据
                //SocketInputStream读取的时候,读取到末尾会阻塞住,不会立刻返回-1,一段时间没有读取到,才会最终返回-1
                //专门开启一个子线程来去处理请求的拆解步骤,主线程只负责去接收客户端的连接
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Request request = new Request(client);
                        //   /1.html   /2.html
                        //如果要把本地硬盘上面去查找文件,那么一定需要用file
                        String requestURI = request.getRequestURI();
                        File file = new File(requestURI.substring(1));
                        OutputStream outputStream = null;
                        try {
                            outputStream = client.getOutputStream();
                            StringBuffer buffer = new StringBuffer();
                            if(file.exists() && file.isFile()){
                                //确保文件的确存在,并且不是目录
                                //状态码应当返回200
                                buffer.append("HTTP/1.1 200 OK\r\n");
                                buffer.append("Content-Type:text/html\r\n");
                                buffer.append("Server: agou\r\n");
                                buffer.append("\r\n");
                                //此时吧响应行、响应头、空行写出去了
                                outputStream.write(buffer.toString().getBytes("utf-8"));
                                //最后在写响应体
                                FileInputStream fileInputStream = new FileInputStream(file);
                                byte[] bytes = new byte[1024];
                                int length = 0;
                                while ((length = fileInputStream.read(bytes)) != -1){
                                    outputStream.write(bytes,0, length);
                                }
                                return;
                            }
                            buffer.append("HTTP/1.1 404 Not Found\r\n");
                            buffer.append("Content-Type:text/html\r\n");
                            buffer.append("\r\n");
                            buffer.append("<div style='color:red'>File Not Found<div>");
                            outputStream.write(buffer.toString().getBytes("utf-8"));
                        } catch (IOException e) {
                            e.printStackTrace();
                        }finally {
                            if(outputStream != null){
                                try {
                                    outputStream.close();
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                }).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @ClassName Request
 * @Description: 把请求报文封装到request对象中
 * @Author 远志 zhangsong@cskaoyan.onaliyun.com
 * @Date 2021/12/6 14:43
 * @Version V1.0
 **/
public class Request {

    private String requestString;

    /**
     * 请求方法
     */
    private String method;

    /**
     * 请求资源
     */
    private String requestURI;

    /**
     * 版本协议
     */
    private String protocol;

    private Map<String, String> requestHeaders = new HashMap<>();

    public Request(Socket client) {
        try {
            InputStream inputStream = client.getInputStream();
            byte[] bytes = new byte[1024];
            int length = inputStream.read(bytes);
            this.requestString = new String(bytes, 0, length);
            parseRequestLine();
            parseRequestHeaders();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     *
     *
     * @title:
     * @createAuthor: 远志
     * @createDate: 2021/12/6 14:55
     * @description: 解析请求头
     * @version: 1.0
     * @return:
     */
    private void parseRequestHeaders() {
        int begin = requestString.indexOf("\r\n");
        int end = requestString.indexOf("\r\n\r\n");
        String substring = requestString.substring(begin + 2, end);
        String[] parts = substring.split("\r\n");
        for (String part : parts) {
            int i = part.indexOf(":");
            String headerName = part.substring(0, i).trim();
            String headerValue = part.substring(i + 1).trim();
            requestHeaders.put(headerName, headerValue);
        }
    }

    /**
     *
     *
     * @title:
     * @createAuthor: 远志
     * @createDate: 2021/12/6 14:46
     * @description:
     * 解析请求行
     * 将请求报文进行拆解,拆分成若干部分
     * 利用换行符来进行拆分\r\n
     * 0-\r\n  请求行
     * \r\n-----\r\n\r\n  请求头
     * @version: 1.0
     * @return:
     */
    private void parseRequestLine() {
        //拿到第一次出现\r\n的下标位置
        int index = requestString.indexOf("\r\n");
        String requestLine = requestString.substring(0, index);
        String[] parts = requestLine.split(" ");
        this.method = parts[0];
        //如果浏览器是以get请求方法发送请求,同时携带了请求参数,那么请求参数会附着在uri中
        //此时如果不把参数去掉,则找不到该文件
        this.requestURI = parts[1];
        this.protocol = parts[2];
        //主要用来判断请求资源中是否有请求参数
        //正常情况下,我们还需要进一步去处理请求参数,我们这里面为了简便,就不去处理了
        int i = requestURI.indexOf("?");
        if(i != -1){
            requestURI = requestURI.substring(0, i);
        }
    }

    public String getMethod() {
        return method;
    }

    public String getRequestURI() {
        return requestURI;
    }

    public String getProtocol() {
        return protocol;
    }

    public String getHeader(String headerName){
        return requestHeaders.get(headerName);
    }

    public Set<String> getHeaderNames(){
        return requestHeaders.keySet();
    }
}

服务器

JavaEE规范:

即JavaEE给服务器的开发者制定了一套接口,为了保证服务器可以进行解耦,在替换的时候方便进行替换,于是所有服务器都需要实现JavaEE制定的标准和接口,这样在使用的时候就不必进行大量的替换操作

Tomcat

安装

bin-启动的目录,根目录

conf-对tomcat进行配置

logs-日志存放目录:可以根据最后的修改时间来排查故障

webapps目录-部署资源

启动

1.bin目录下执行startup.bat文件

2.在bin目录下唤出cmd,执行startup

部署资源
直接部署

​ 直接将资源放在tomcat的webapps目录下

​ 注意:在tomcat中,不管是直接部署还是虚拟映射,tomct的最小资源单位是应用,所以需要将文件放在一个应用中。相当于是每个文件都需要一个包名,tomcat根据包名来进行识别

​ 访问的方法:当输入http://localhost:8080时,此时相当于已经定位到了tomcat的webapps目录,接下来只需要写出相对路径关系即可,即使用应用名来访问。

​ 例如:http://localhost:8080/36th/1.txt

​ 除此之外,可以部署一个war包,tomcat会自动解压形成目录,相当于jar包的形式,本质上还是个压缩包

虚拟映射

​ 不把文件放在webapps目录下,随意放置;

​ 本质上客户端是从服务器的磁盘中获取内容,所以理论上来说所有的磁盘路径都可以读取到

1.在conf/Catalina/localhost目录下,配置一个xml文件

<?xml version="1.0" encoding="UTF-8"?>
<Context docBase="D:/app" />

tomcat的最小单位是应用,而应用有两个属性:应用名与路径

Context path来表示应用名 docBase 应用的路径

配置完虚拟映射后相当于将webapps的目录换成了我们自定义的目录,那我们如果直接输入localhost就相当于定位到了该目录,此时访问资源就只需要相对于这个目录来访问即可

2.conf/server.xml文件中配置Context节点(了解)

Host节点下配置Context节点

<Context path="/app362" docBase="D:\app" />

不推荐使用,因为修改了主配置文件容易产生问题

Tomcat组件

tomcat本身是由一系列的可插拔的组件组成的,主要是在server.xml文件中配置,tomcat在启动的时候会自动读取xml文件里面的内容,将每个节点依次解析为一个组件(对象)

<Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />

以Connector为例,tomcat在启动时,会读取xml文件里面的配置,根据这些配置信息实例化一个Connector对象出来,该对象会监听8080端口号,主要的职责就是将HTTP/1.1协议的请求报文解析成为request对象

Tomcat请求处理流程

以访问http://localhost:8080/app36/1.txt为例

1.地址栏输入对应的地址,首先进行域名的解析(浏览器、操作系统、hosts、DNS服务器)拿到ip地址

2.TCP三次握手建立连接

3.浏览器生成请求报文,经过tcp层拆包,打上tcp头标签,经过ip层打上ip标签,本机和目标的ip地址端口号

4.从链路层出去,在网络上进行中转传输,到达目标主机后先经过ip层拆掉标签,然后经过tcp层拆掉标签完成对源文件的重新组装

5.HTTP报文被一直监听8080端口的Connector接收到,将报文封装成request对象,同时提供一共response对象,用于返回响应报文

6.Connector对象将这两个对象传给Engine,engin进一步下发给Host来对对象进行处理

7.Host的职责是挑选一个合适的Context,尝试去找app36的应用(webapps、conf/Catalina/localhost、server.xml),如果找到了就将这两个对象进一步交给应用来处理,如果没有找到会执行ROOT下的默认文件

8.到达应用之后,有效的路径是/1.txt,利用docBase+/1.txt查找该文件是否存在;找到则将该文件的内容写入到response中,找不到则写入404,这里要注意即使是返回404,我们也认为这次返回是一次有效的返回

9.Connector读取response中的数据,按照HTTP/1.1的格式要求生成响应报文

这里注意:如果文件在tocmat的webapps目录里面,应用名(path)会取目录的名称, 应用的路径(docBase):webapps路径 + 应用名

Tomcat的设置

1.设置端口号

有些网站在访问的时候是不用带端口号的,这是因为他们使用的是当前协议默认的端口号

http协议是80端口号

https协议是443端口号

需要设置tomcat监听80端口号

<Connector port="80" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />

2.设置默认访问的应用

tomcat中有一个缺省的应用,如果找不到其他应用了,就会交给缺省的来处理

应用名叫做ROOT

在访问的时候不需要加应用名就能访问

比如ROOT应用下有一个1.txt,那么访问http://localhost:8080/1.txt

http://localhost:8080/app37/1.txt,将其交给ROOT应用,然后在该应用中去查找/app37/1.txt文件

如果需要访问资源并且不携带应用名,可以直接设置当前资源所在的应用为ROOT

1.直接部署,将应用名改为ROOT,原来的ROOT换一个名字

2.虚拟映射,创建一个xml文件,将名字改成ROOT.xml即可,会覆盖webapps里的ROOT

3.设置欢迎页面

最终的访问地址是一个目录而不是具体的文件的时候,tomcat会按照如下的顺序依次去查找文件,如果找到则加载,如果找不到就返回一个404

无论如何,最终我们解析到的都是一个硬盘上的目录,那么如果只输入localhost的话,最后就只会定义到ROOT目录下,但ROOT目录下有很多个资源,于是就会根据以下的设置来进行访问

<welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值