《深度解析Tomcat》 第一章 一个简单的Web服务器

本章介绍Java Web服务器是如何运行的。从中可以知道Tomcat是如何工作的。

基于Java的Web服务器会使用java.net.Socket类和java.net.ServerSocket类这两个类,并通过发送HTTP消息进行通信。

因此,本章先介绍HTTP协议和这两个类。然后介绍一个简单的Web服务器。

1.1 HTTP协议

HTTP请求和相应信息

略。

1.2 Socket类

1.2.1 Socket类

Socket类表示一个客户端套接字(socket),即想要连接远程服务器应用程序时创建的套接字。

public Socket(String host, int port);//参数host是远程主机的名称或IP地址,参数port是连接远程应用程序的端口号。
// 例如,想要通过80端口连接yahoo.com,可以使用以下语句创建Socket对象:
new Socket("yahoo.com". 80);

一旦成功创建了Socket类的实例,就可以使用该实例发送或接收字节流。代码示例:

package demo;

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

public class SocketDemo {
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("127.0.0.1", 8080);
            OutputStream os = socket.getOutputStream();
            boolean autoFlush = true;
            PrintWriter out = new PrintWriter(socket.getOutputStream(), autoFlush);
            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            // 发送一个HTTP请求到服务器
            out.println("GET /index.jsp HTTP/1.1");
            out.println("Host: localhost:8080");
            out.println("Connection: Close");
            out.println();
            // 读取响应
            boolean loop = true;
            StringBuffer sb = new StringBuffer(8096);
            while (loop) {
                if (in.ready()) {
                    int i=0;
                    while (i != -1) {
                        i = in.read();
                        sb.append((char) i);
                    }
                    loop = false;
                }
                Thread.currentThread().sleep(50);
            }
            //展示响应信息
            System.out.println(sb.toString());
            socket.close();
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }


    }
}

得到结果:

HTTP/1.1 200 
Content-Type: text/html;charset=UTF-8
Transfer-Encoding: chunked
Date: Thu, 26 Sep 2019 10:21:08 GMT
Connection: close

2000

(以及Tomcat的index.jsp页面代码)

1.2.2 ServerSocket类

 ServerSocket类和Socket类并不相同。服务器套接字必须时刻待命,它不知道客户端应用程序会在什么时候发起连接。ServerSocket类的构造函数需要指明IP地址和服务器套接字侦听的端口号。

ServerSocket类的其中一个构造函数的签名如下:

public SeverSocket (int port, int backLog, InetAddress bindingAddress);

创建了ServerSocket实例后,可以使其等待传入的连接请求,该连接请求会通过服务器套接字侦听的端口上绑定地址传入。这些工作可以通过调用ServerSocket类的accept方法完成。只有当接收到连接请求后,该方法才会返回,其返回值是一个Socket实例。可以使用该Socket对象与客户端应用程序进行字节流的发送/接收。

1.3 应用程序

针对以上知识点,做一个简单的web服务器。HttpServer类相当于一个已经启动了的Tomcat,Request类模拟访问URL,Response类模拟返回消息。

package webDemo;

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

public class HttpServer {
    // WEB_ROOT静态资源存放目录
    public static final String WEB_ROOT =
            System.getProperty("user.dir") + File.separator + "webRoot";
    // 关闭
    public static final String SHUTDOWN_COMMAND = "/SHUTDOWN";
    // 是否接收到关闭指令
    private boolean shutdown = false;

    public static void main(String[] args) {
        HttpServer server = new HttpServer();
        server.await();
    }

    private void await() {
        ServerSocket serverSocket = null;
        int port = 8081;
        try {
            serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }

        while (!shutdown) {
            Socket socket;
            InputStream input;
            FileOutputStream output;

            try {
                socket = serverSocket.accept();
                input = socket.getInputStream();
                output = (FileOutputStream) socket.getOutputStream();
                // 创建请求的对象和参数
                Request request = new Request(input);
                request.parse();
                // 创建响应对象
                Response response = new Response(output);
                response.setRequest(request);
                response.sendStaticResource();
                // 关闭Socket
                socket.close();
                // 如果URL是shutdown命令
                shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
            } catch (Exception e) {
                e.getStackTrace();
                continue;
            }
        }
    }
}
package webDemo;

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

public class Request {
    private InputStream input;
    private String uri;

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

    public String getUri() {
        return uri;
    }

    /**
     *  从InputStream对象中读取整个字节流
     */
    public void parse() {
        StringBuffer request = new StringBuffer(2048);
        int i;
        byte[] buffer = new byte[2048];
        try {
            i = input.read(buffer);
        } catch (IOException e) {
            e.getStackTrace();
            i = -1;
        }
        for (int j = 0; j < i; j++) {
            request.append((char) buffer[j]);
        }
        System.out.println("request == " + request.toString());
        uri = parseUri(request.toString());
    }

    /**
     * GET /index.html HTTP/1.1
     * 该方法在请求行中搜索第一个和第二个空格,从而找出URI
     * @param requestString
     * @return
     */
    private String parseUri(String requestString) {
        int index1;
        int index2 = 0;
        index1 = requestString.indexOf(' ');
        if (index1 != -1) {
            index2 = requestString.indexOf(' ', index1 + 1);
        }
        if (index2 > index1) {
            return requestString.substring(index1 + 1, index2);
        }
        return null;
    }
}
package webDemo;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import java.io.*;

/*
    HTTP Response = Status-Line
        *( (general - header | response-header | entity-header) CRLF)
        CRLF
        [ message-body ]
        Status-Line - HTTP-Version SP Status-Code SP Reason-Phrase CRLF
 */
public class Response {
    Request request;
    OutputStream output;

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

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

    /**
     * 发送静态资源
     * @throws IOException
     */
    public void sendStaticResource() throws IOException {
        try {
            File file = new File(HttpServer.WEB_ROOT, request.getUri());//目录+文件名
            if (file.exists()) {
                BufferedReader reader = new BufferedReader(new FileReader(file));
                StringBuffer sb = new StringBuffer();
                String line;
                while ((line = reader.readLine()) != null) {
                    sb.append(line).append("\r\n");
                }
                StringBuffer result = new StringBuffer();
                result.append("HTTP/1.1 200 ok \r\n");
                result.append("Content-Language:zh-CN \r\n");
                // charset=UTF-8 解决中文乱码问题
//                result.append("Content-Type:text/html;image/gif;charset=UTF-8 \r\n");
                result.append("multipart/x-mixed-replace; boundary=myboundary");
                result.append("Content-Length:" + file.length() + "\r\n");
                result.append("\r\n" + sb.toString());
                output.write(result.toString().getBytes());
                output.flush();
                output.close();
            } else {
                //如果没有文件
                String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
                        "Content-Type: text/html\r\n" +
                        "Content-Length: 23\r\n" +
                        "\r\n" +
                        "<h1>File Not Found</h1>";
                output.write(errorMessage.getBytes());
            }
        } catch (Exception e) {
            System.out.println("----" + e.toString());
        }
    }

    private void covertImag(String line) {
        BASE64Decoder decoder = new BASE64Decoder();
        try {
            byte[] b = decoder.decodeBuffer(line);
            for (int i = 0; i < b.length; i++) {
                if (b[i] < 0) {
                    b[i] += 256;
                }
            }
            output.write(b);
            output.flush();
            output.close();
        } catch (IOException e) {
            System.out.println(e.toString());
        }
    }
}

 index.html页面的代码:

<html>
    <head>
        <title>Welcome to BrainySoftware</title>
    </head>
    <body>
        <img src="./images/logo.gif">
        <br>
        Welcome to BrainySoftware.
    </body>
</html>

该html页面中包含一个图片,但是我的代码无法显示代码,暂时没有找到解决办法,留着坑以后填。如果有大神知道如何处理,非常感激能教我。

猜你喜欢

转载自www.cnblogs.com/tzzt01/p/11596114.html