【Javaweb】模拟实现HTTP服务器

目录

一,HTTP 协议的工作过程

二、HTTP协议格式

1,抓包分析搜狗主页

2,协议格式总结

三、版本V1

四、版本V2

1,创建 HttpRequest 类

2,创建 HttpResponse 类

3,创建 HttpServer 类

五、版本V3

1. 创建 HttpRequest 类

2,创建 HttpResponse 类

3,创建 HttpServer 类

4,insex.html


一,HTTP 协议的工作过程

二、HTTP协议格式

1,抓包分析搜狗主页

HTTP请求

首行: [方法] + [url] + [版本]

Header: 请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;遇到空行表示Header部 分结束 Body: 空行后面的内容都是Body.

Body允许为空字符串. 如果Body存在, 则在Header中会有 一个Content-Length属性来标识Body的长度

HTTP响应

首行: [版本号] + [状态码] + [状态码解释]

Header: 请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;遇到空行表示Header部 分结束

Body: 空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有 一个Content-Length属性来标识Body的长度; 如果服务器返回了一个html页面, 那么html页 面内容就是在body中

2,协议格式总结

三、版本V1

        实现一个最简单的 HTTP 服务器.

        在这个版本中, 我们只是简单解析 GET 请求, 并根据请求的路径来构造出不同的响应.

        路径为 /200, 返回一个 "欢迎页面".

        路径为 /404, 返回一个 "没有找到" 的页面.

        路径为 /302, 重定向到其他的页面

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class HttpServerV1 {
    // HTTP 底层要基于 TCP 来实现. 需要按照 TCP 的基本格式来先进行开发.
    private ServerSocket serverSocket = null;

    public HttpServerV1(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动");
        ExecutorService executorService = Executors.newCachedThreadPool();
        while (true) {
            // 1. 获取连接
            Socket clientSocket = serverSocket.accept();
            // 2. 处理连接(使用短连接的方式实现)
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    process(clientSocket);
                }
            });
        }
    }

    private void process(Socket clientSocket) {
        // 由于 HTTP 协议是文本协议, 所以仍然使用字符流来处理.
        try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
             BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()))) {
            // 下面的操作都要严格按照 HTTP 协议来进行操作.
            // 1. 读取请求并解析
            //  a) 解析首行, 三个部分使用空格切分
            String firstLine = bufferedReader.readLine();
            String[] firstLineTokens = firstLine.split(" ");
            String method = firstLineTokens[0];
            String url = firstLineTokens[1];
            String version = firstLineTokens[2];
            //  b) 解析 header, 按行读取, 然后按照冒号空格来分割键值对
            Map<String, String> headers = new HashMap<>();
            String line = "";
            // readLine 读取的一行内容, 是会自动去掉换行符的. 对于空行来说, 去掉了换行符, 就变成空字符串
            while ((line = bufferedReader.readLine()) != null && line.length() != 0) {
                // 不能使用 : 来切分. 像 referer 字段, 里面的内容是可能包含 : .
                String[] headerTokens = line.split(": ");
                headers.put(headerTokens[0], headerTokens[1]);
            }
            //  c) 解析 body (暂时先不考虑)
            // 请求解析完毕, 加上一个日志, 观察请求的内容是否正确.
            System.out.printf("%s %s %s\n", method, url, version);
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                System.out.println(entry.getKey() + ": " + entry.getValue());
            }
            System.out.println();
            // 2. 根据请求计算响应
            // 不管是啥样的请求, 咱们都返回一个 hello 这样的 html
            String resp = "";
            if (url.equals("/200")) {
                bufferedWriter.write(version + " 200 OK\n");
                resp = "<h1>hello</h1>";
            } else if (url.equals("/404")) {
                bufferedWriter.write(version + " 404 Not Found\n");
                resp = "<h1>not found</h1>";
            } else if (url.equals("/302")) {
                bufferedWriter.write(version + " 303 Found\n");
                bufferedWriter.write("Location: http://www.sogou.com\n");
                resp = "";
            } else {
                bufferedWriter.write(version + " 200 OK\n");
                resp = "<h1>default</h1>";
            }
            // 3. 把响应写回到客户端
            bufferedWriter.write("Content-Type: text/html\n");
            bufferedWriter.write("Content-Length: " + resp.getBytes().length + "\n"); // 此处的长度, 不能写成 resp.length(), 得到的是字符的数目, 而不是字节的数目
            bufferedWriter.write("\n");
            bufferedWriter.write(resp);
            // 此处这个 flush 就算没有, 问题也不大. 紧接着
            // bufferedWriter 对象就要被关闭了. close 时就会自动触发刷新操作.
            bufferedWriter.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                clientSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws IOException {
        HttpServerV1 server = new HttpServerV1(9090);
        server.start();
    }
}

 

  • 31
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

-孤单又灿烂的神-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值