java.简易版Http Server

这篇博客介绍了如何使用Java编写一个简易的Http Server,通过源码解析了HttpServerTest和RequestTest两个关键类的功能和实现原理。
摘要由CSDN通过智能技术生成

源码:

HttpServerTest类

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

public class HttpServerTest {

    private static final int PORT = 9999;
    // 获取处理器核数
    private static final int COUNT = Runtime.getRuntime().availableProcessors();
    // 创建线程是有消耗的,不是线程越多处理效率越高,而是要有一个合适的线程数来处理
    // 处理的任务量和线程数量、CPU、内存等资源都相关
    // 一般推荐处理器核数数量的线程作为固定线程池内的线程数量
    private static final ExecutorService EXE = Executors.newFixedThreadPool(COUNT);

    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(PORT);
        while (true) {
            // 一个 socket 对象代表一个客户端
            // 获取客户端请求 socket 对象:阻塞方法
            Socket socket = server.accept();
            EXE.submit(new HttpTask(socket));
        }
    }
}

// Http 请求任务处理类
class HttpTask implements Runnable {

    // 客户端连接
    private Socket socket;

    public HttpTask(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        InputStream is = null;
        InputStreamReader isr = null;
        BufferedReader br = null;
        OutputStream os = null;
        // 输出流的包装
        // 打印
        PrintWriter pw = null;
        try {
            try {
                // 获取客户端请求数据:输入流
                is = socket.getInputStream();
                // 转换流,转换成字符流
                isr = new InputStreamReader(is, "UTF-8");
                // 在缓冲字符流中读取,提高效率
                br = new BufferedReader(isr);

                RequestTest requestTest = new RequestTest(); // 初始化请求对象
                // 请求数据的解析:http 协议报的解包
                // 1.解析请求行(第一行):method url version
                String requestLine = br.readLine();
                String[] requestLines = requestLine.split(" ");
                requestTest.setMethod(requestLines[0]);
                // 可能为 http://Localhost:9999/xxx?username=stu&password=123
                // ?问号前的放到 url 里面去,?问号之后的放到请求参数里面去
                String url = requestLines[1];
                if (url.contains("?")) {
                    String parameters = url.substring(url.indexOf("?") + 1);
                    requestTest.parseParameters(parameters);
                    url = url.substring(0, url.indexOf("?"));
                }
                requestTest.setUrl(url);
                requestTest.setVersion(requestLines[2]);

                // 2.解析请求头:key : value 每个换行,以空行作为结尾
                String header;
                // 通过流来读取
                while ((header = br.readLine()) != null
                        && header.length() != 0) {
                    // 既不是空行也不是空值
                    // 代表从0到冒号:结束
                    String key = header.substring(0, header.indexOf(":"));
                    String value = header.substring(header.indexOf(":") + 1);
                    // trim() 方法去掉空格
                    requestTest.addHeader(key, value.trim());
                }
                // POST请求,需要根据请求头 Content-Length 获取请求体的长度
                if ("POST".equals(requestTest.getMethod())) {
                    String len = requestTest.getHeader("Content-Length");
                    if (len != null) {
                        int l = Integer.parseInt(len);
                        char[] chars = new char[l];
                        br.read(chars, 0, l);
                        // 请求参数格式:username=stu&password=123
                        String requestBody = new String(chars);
                        requestTest.parseParameters(requestBody);
                    }
                }
                System.out.println(requestTest);
                // 获取客户端输出流,返回响应数据
                os = socket.getOutputStream();
                // PrintWriter 是打印流,需要发送到缓冲区的,
                // 所以需要在缓冲区里自动刷新一下
                pw = new PrintWriter(os, true);
                // http://Localhost:9999/302/111
                // 只找 /302/111,所以要去掉前面的部分
                if ("/302".equals(requestTest.getUrl())) {
                    pw.println("HTTP/1.1 302 重定向");
                    pw.println("Content-Type:text/html;charset=utf-8");
                    pw.println("Location: http://www.baidu.com");
                }
            } finally {
                // 反向关闭
                if (br != null) {
                    br.close();
                }
                if (isr != null) {
                    isr.close();
                }
                if (is != null) {
                    is.close();
                }
                if (pw != null) {
                    pw.close();
                }
                if (os != null) {
                    os.close();
                }
                socket.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}


RequestTest类:

import java.util.HashMap;
import java.util.Map;

public class RequestTest {
    private String method; // 请求方法
    private String url; // 请求路径
    private String version; // http 版本号
    private Map<String, String> headers = new HashMap<>(); // 保存请求头
    private Map<String, String> parameters = new HashMap<>(); // 保存请求参数

    // 添加请求头
    public void addHeader(String key, String value) {
        headers.put(key, value);
    }

    // 获取某个请求头
    public String getHeader(String key) {
        return headers.get(key);
    }

    @Override
    public String toString() {
        return "RequestTest{" +
                "\n method='" + method + '\'' +
                ",\n url='" + url + '\'' +
                ",\n version='" + version + '\'' +
                ",\n headers=" + headers +
                ",\n parameters=" + parameters +
                '}';
    }

    // 解析请求参数 key1=value1&key2=value2
    public void parseParameters(String parameters) {
        // 暂时没有考虑参数的判断
        String[] ps = parameters.split("&");
        for (String p : ps) {
            String[] array = p.split("=");
            addParameter(array[0], array[1]);
        }
    }
    // 添加请求参数
    public void addParameter(String key, String value) {
        parameters.put(key, value);
    }

    // 获取请求参数
    public String getParameter(String key) {
        return parameters.get(key);
    }

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    public Map<String, String> getHeaders() {
        return headers;
    }

    public void setHeaders(Map<String, String> headers) {
        this.headers = headers;
    }

    public Map<String, String> getParameters() {
        return parameters;
    }

    public void setParameters(Map<String, String> parameters) {
        this.parameters = parameters;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值