Java implementation can get simple static resource server (like Tomcat)

Java implementation can get simple static resource server (like Tomcat)

We usually use Tomcatas a server for Webdevelopers usually enter http://localhost:8888/index.htmlthis URL, such a request Request Headersis as follows:

GET /index.html HTTP/1.1
Host: localhost:8888
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36
Sec-Fetch-User: ?1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8

From the Request Headersget request to access the desired resource is static index.html(we own realization server, the browser initiates the request, the server is the background to get Request Headersthis data).

Now we know how to get the desired resource requests (from Request Headersacquisition), which is easier to handle.

When the user enters in the browser URLto initiate a request, in fact, to establish a network connection to the server, and then access resources on the server.
This Java network programming and a relationship, and now talk about the main task server:

  • To open the server first ServerSocket, when there is a client (browser) to connect, and initiate a request to obtain the request Request Headers(you can browser as a multi-user chat room before we realized inside the client, because the browser to the server initiates the request, also through the data stream Streamor similar components implemented).
  • Server gets the user's request Request Headersafter, and from the Request Headersanalysis of the resource name.
  • After the user wants to know the resources, the server resources with the necessary information package sent to the client (protocol status code, resources, etc.), when the resource does not exist, the server returns 404.htmlthe resource.

BootstrapClass, start the server module.

import connector.Connector;

public final class Bootstrap {
    public static void main(String[] args) {
        Connector connector = new Connector();
        connector.start();
    }
}

ConnectorClass is created ServerSocket(establish a connection for a client), creating Request(from Request Headersaccess to resources in the client request), create Response(the protocol status code, and other resources in the data stream packet writing), to create StaticProcessor(by means of Responsethe necessary resources and information sent to the server), code comments should be very clear.

package connector;

import processor.StaticProcessor;

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

public class Connector implements Runnable {

    private static final int DEFAULT_PORT = 8888;

    private ServerSocket server;
    private int port;

    public Connector(){
        this(DEFAULT_PORT);
    }

    public Connector(int port) {
        this.port = port;
    }

    public void start(){
        new Thread(this).start();
    }

    @Override
    public void run() {
        try {
            // 创建ServerSocket,绑定、监听端口
            server = new ServerSocket(port);
            System.out.println("启动服务器,监听端口:" + port);
            while(true){
                // 等待客户端连接
                Socket socket = server.accept();
                // 获取输入流
                InputStream input = socket.getInputStream();
                // 获取输出流
                OutputStream output = socket.getOutputStream();

                // 创建请求request,并且传入输入流(有客户端请求的信息)
                Request request = new Request(input);
                // request通过输入流的信息,分析出客户端想要的资源
                request.parse();

                // 创建响应response,并且传入输出流(方便将获取的资源发送给客户端)
                Response response = new Response(output);
                // response需要request的uri(客户端请求的资源)
                response.setRequest(request);
                
                // 创建处理者processor
                StaticProcessor processor = new StaticProcessor();
                // processor通过response把数据发送给客户端
                processor.process(response);

                //关闭socket
                close(socket);
            }
        } catch (IOException e) {
            // 浏览器可以识别状态码,当状态码表示请求不成功时(如404),似乎会断开socket,所以这里不进行处理
        } finally{
            close(server);
        }
    }

    private void close(Closeable closeable){
        if(closeable != null){
            try {
                closeable.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

RequestClass, to establish a connection with the server from the client socket's input stream InputStreamto read the information, and analyze the resources on the client wants.

package connector;

import java.io.IOException;
import java.io.InputStream;

public class Request {

    private static final int BUFFER_SIZE = 1024;

    private InputStream input;

    private String uri;

    public Request(InputStream input) {
        this.input = input;
    }

    public String getUri() {
        return uri;
    }

    public void parse(){
        int length = 0;
        byte[] buffer = new byte[BUFFER_SIZE];
        try {
            // 读取流里面的数据,并且记录长度
            length = input.read(buffer);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 将数据转化成StringBuilder
        StringBuilder request = new StringBuilder();
        for (int i = 0; i < length; i++) {
            request.append((char) buffer[i]);
        }

        // 分析出客户端想要的资源
        uri = parseUri(request.toString());
    }

    /**
     *从 “GET /index.html HTTP/1.1 
     *      ......
     *       ”中获取index.html
     * 通过空格来分离出来
     * */
    public String parseUri(String request){
        int index1 , index2;
        // 第一个空格的位置
        index1 = request.indexOf(' ');
        if(index1 != -1){
            // 第二个空格的位置
            index2 = request.indexOf(' ', index1+1);
            if(index2 != -1){
                // 分离出资源名称
                return request.substring(index1 + 2 , index2);
            }
        }
        // 没有办法解析出uri
        return "";
    }
}

ResponseClass, the protocol, status code, and other resources in the data stream packet writing.

package connector;

import java.io.*;

public class Response {

    private static final int BUFFER_SIZE = 1024;
    Request request;
    OutputStream output;

    public Response(OutputStream output) {
        this.output = output;
    }

    public void setRequest(Request request) {
        this.request = request;
    }

    public void sendStaticResource() throws IOException {
        try{
            // 通过request的uri,获取资源的路径
            String filePath = getClass()
                    .getClassLoader()
                    .getResource(request.getUri()).getFile();
            // 创建资源文件
            File file = new File(filePath.substring(1 , filePath.length()));
            // 将资源写入流里面,HttpStatus.SC_OK是状态码
            write(file , HttpStatus.SC_OK);
        } catch (Exception e) {
            // 当出现错误时,简单处理 ,发送404.html给客户端
            String errorFilePath = getClass().getClassLoader().getResource("404.html").getFile();
            // 将资源写入流里面,HttpStatus.SC_NOT_FOUND是状态码
            write(new File(errorFilePath.substring(1 , errorFilePath.length())) ,
                    HttpStatus.SC_NOT_FOUND);

        }
    }

    private void write(File resource , HttpStatus status) throws IOException {
        
        try(FileInputStream fis = new FileInputStream(resource)){
            // 先将协议、状态码等必要信息写入流中,ConnectorUtils是工具类
            output.write(ConnectorUtils.renderStatus(status).getBytes());
            byte[] buffer = new byte[BUFFER_SIZE];
            int length = 0;
            // 把资源文件写入流中
            while((length = fis.read(buffer , 0 , BUFFER_SIZE)) != -1){
                output.write(buffer , 0 ,length);
            }
        }
    }
}

HttpStatusClass, status code enumeration class.

package connector;

public enum HttpStatus {
    SC_OK(200 , "OK"),
    SC_NOT_FOUND(404 , "File Not Found");

    private int statusCode;
    private String reason;

    HttpStatus(int statusCode, String reason) {
        this.statusCode = statusCode;
        this.reason = reason;
    }

    public int getStatusCode() {
        return statusCode;
    }

    public String getReason() {
        return reason;
    }
}

ConnectorUtilsInfo (tools), the protocol, the status code information can be assembled into a browser to recognize.

package connector;

public class ConnectorUtils {

    public static final String PROTOCOL = "HTTP/1.1";

    public static final String CARRIAGE = "\r";

    public static final String NEWLINE = "\n";

    public static final String SPACE = " ";

    public static String renderStatus(HttpStatus status){
        StringBuilder sb = new StringBuilder(PROTOCOL)
                .append(SPACE)
                .append(status.getStatusCode())
                .append(SPACE)
                .append(status.getReason())
                .append(CARRIAGE).append(NEWLINE)
                .append(CARRIAGE).append(NEWLINE);
        return sb.toString();
    }
}

StaticProcessorClass, as static resources to deal with the identity of persons, by means of Responseprocessing.

package processor;

import connector.Response;

import java.io.IOException;

public class StaticProcessor {

    public void process(Response response){
        try {
            response.sendStaticResource();
        } catch (IOException e) {
            // 不处理浏览器断开连接等错误
        }
    }
}

Here we will complete the static resources can get a simple version of the server implemented in Java.

Project construction
Here Insert Picture Description
project source code

Java implementation server to obtain static resources

test

Request succeeds, the server has the resources.
Here Insert Picture Description
Here Insert Picture Description

The request failed server does not have the resources.

Here Insert Picture Description

Here Insert Picture Description

You can try hands-on, do not ServerSocketbe achieved with the components NIO AIO models or models to achieve.

If there is something wrong place, please let us know (remember the message Oh ~ ~ ~ ~).

Published 288 original articles · won praise 325 · Views 650,000 +

Guess you like

Origin blog.csdn.net/qq_37960603/article/details/104216019