《深度解析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页面中包含一个图片,但是我的代码无法显示代码,暂时没有找到解决办法,留着坑以后填。如果有大神知道如何处理,非常感激能教我。

posted on 2019-09-28 19:47  tzzt01 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/tzzt01/p/11596114.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个简易的Tomcat服务器的实现: 1. 创建一个Java项目,命名为TomcatServer。 2. 在项目中创建一个名为Server的类,这个类将作为Tomcat服务器的启动入口。 ```java public class Server { public static void main(String[] args) { try { // 创建一个ServerSocket,监听端口号为8080 ServerSocket serverSocket = new ServerSocket(8080); System.out.println("Tomcat Server is running on port 8080..."); // 不断接受客户端的请求 while (true) { Socket clientSocket = serverSocket.accept(); // 创建一个新的线程来处理客户端请求 Thread thread = new Thread(new ClientHandler(clientSocket)); thread.start(); } } catch (IOException e) { e.printStackTrace(); } } } ``` 3. 创建一个名为ClientHandler的类,这个类将处理客户端的请求。 ```java public class ClientHandler implements Runnable { private Socket clientSocket; public ClientHandler(Socket clientSocket) { this.clientSocket = clientSocket; } @Override public void run() { try { // 读取客户端的请求内容 BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); String line = reader.readLine(); // 解析请求的URI String uri = line.split(" ")[1]; // 如果URI为/,返回一个简单的HTML页面 if ("/".equals(uri)) { String response = "HTTP/1.1 200 OK\n"; response += "Content-Type: text/html; charset=utf-8\n\n"; response += "<html><body><h1>Hello, World!</h1></body></html>"; OutputStream outputStream = clientSocket.getOutputStream(); outputStream.write(response.getBytes()); outputStream.flush(); } // 关闭客户端Socket clientSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } ``` 4. 运行Server类,打开浏览器,访问http://localhost:8080/,即可看到一个简单的Hello, World页面。 注意:这个Tomcat服务器只是一个简单的示例,实际使用时需要考虑更多的安全性和性能方面的问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值