小项目:Java版本精简Tomcat

本文介绍了一款基于Java的精简版Tomcat服务器项目,实现了静态文件服务、动态接口及GET方法支持。项目运用了网络编程、面向对象、HTTP协议和多线程技术,通过SocketAPI建立连接,解析HTTP请求并生成响应。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

项目名称:Java版本精简Tomcat

项目功能描述:

                     1.实现静态文件服务器

                     2.动态实现接口

                     3.支持方法:Get方法

涉及技术:

               1.网络编程(Java基础语法,Socket API)

               2.面型对象的接口多态实现

               3.HTTP协议

               4.多线程技术

 项目实现:

                1.服务器端建立Socket链接

                2.读取请求数据

                         2.1 解析请求数据,包装成请求对象

                3.按照业务逻辑处理

                         3.1 不同的URL进行不同的处理

                4.处理响应数据,包装成响应对象

 

将HTTP协议抽象成Java的三个类(请求类,响应类,处理器类),方法使用get()方法

 

处理器类:

             服务器建立Socket链接,绑定ip地址和端口号,由于每次只接受一个客户端请求效率太慢,使用 newFixedThreadPool() 线程池来实现多线程

package com.bittech.httpd.core;

/**
 * HTTP 服务器
 */

import com.bittech.httpd.Handler.*;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

public class HttpServer {

    private final ExecutorService executor = Executors.newFixedThreadPool(
            Runtime.getRuntime().availableProcessors() * 2,
            new ThreadFactory() {

                private final AtomicInteger count = new AtomicInteger(0);
                @Override
                public Thread newThread(Runnable r) {
                    Thread thread = new Thread(r);
                    thread.setName("Thread-Handler-"+count.getAndIncrement());
                    return thread;
                }
            }
    );

    public void start(){
        try {
            ServerSocket serverSocket = new ServerSocket(80);
            System.out.println("Server start on 127.0.0.1:80,Please visited http://127.0.0.1:80/");
            //分发处理器
            DispatcherHandler dispatcherHandler = new DispatcherHandler();
            this.loadHandler(dispatcherHandler);
            while(true){
                Socket socket = serverSocket.accept();
                //线程池   outputStream.write
                executor.execute(dispatcherHandler.handler(socket));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * 新写的 Handler 处理器都在这里加载
     */
    private void loadHandler(DispatcherHandler dispatcherHandler){

        //业务
        dispatcherHandler.registerHandler("/",IndexHandler.class);
        dispatcherHandler.registerHandler("/sum",SumHandler.class);
        dispatcherHandler.registerHandler("/info",InfoHandler.class);

        //内置
        dispatcherHandler.registerHandler("_default_",StaticHandler.class);
        dispatcherHandler.registerHandler("404",NotFoundHandler.class);
    }



}

  请求类:

package com.bittech.httpd.core;

/**
 * 默认Request 实现类
 * 对于Request参数,首行,header,参数
 */

import com.bittech.httpd.common.HttpMethod;
import com.bittech.httpd.common.HttpRequest;

import java.util.List;
import java.util.Map;

public class DefaultHttpRequest implements HttpRequest {

    private final Map<String,String> line;

    private final Map<String,String> header;

    private final Map<String,List<String>> params;

    public DefaultHttpRequest(Map<String, String> line, Map<String, String> header, Map<String, List<String>> params) {
        this.line = line;
        this.header = header;
        this.params = params;
    }

    @Override
    public HttpMethod method() {
        return HttpMethod.lookup(line.get("method"));
    }

    @Override
    public String url() {
        return line.get("url");
    }

    @Override
    public String version() {
        String version = line.get("version");
        return version == null? "HTTP/1.1" : version;
    }

    @Override
    public Map<String, String> header() {
        return header;
    }

    @Override
    public Map<String, List<String>> params() {
        return params;
    }
}

响应类:

       根据HTTP协议响应报文的格式拼接出响应报文.

package com.bittech.httpd.core;

/**
 * 默认 Response 实现类
 */


import com.bittech.httpd.common.HttpRequest;
import com.bittech.httpd.common.HttpResponse;
import com.bittech.httpd.common.HttpStatus;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

public class DefaultHttpResponse implements HttpResponse {

    private final Socket socket;

    private final HttpRequest request;

    private HttpStatus httpStatus = HttpStatus.OK;

    private Map<String,String> header = new HashMap<>();

    private ByteArrayOutputStream content = new ByteArrayOutputStream();

    public DefaultHttpResponse(Socket socket, HttpRequest request) {
        this.socket = socket;
        this.request = request;
    }


    @Override
    public void setHttpStatus(HttpStatus httpStatus) {
        this.httpStatus = httpStatus;
    }

    @Override
    public void setHeader(String key, String value) {
        this.header.put(key,value);
    }

    @Override
    public void setContentType(String value) {
        this.header.put("Content-Type",value);
    }

    @Override
    public void write(byte[] value) {
        try {
            content.write(value);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void write(String value) {
        try {
            content.write(value.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void flush() {
        // HTTP/1.1 200 ok

        // header1 : value1
        // header2 : value2

        //data
        try {
            OutputStream outputStream = socket.getOutputStream();
            outputStream.write(
                    encodeContent(
                            request.version()
                            + " " +
                            this.httpStatus.getRequestStatus()
                            + " "
                            +this.httpStatus.getRequestStatus()
                            + "\r\n"
                    )
            );

            for(Map.Entry<String,String> entry : header.entrySet()){
                outputStream.write(encodeContent(entry.getKey()+ ": "
                        + entry.getValue()+"\r\n"));
            }
            outputStream.write(encodeContent("\r\n"));

            ByteArrayInputStream inputStream = new ByteArrayInputStream(content.toByteArray());

            byte[] buff = new byte[1024];
            int len;
            while((len = inputStream.read(buff))!=-1){
                outputStream.write(buff,0,len);
            }
            socket.close();

        } catch (IOException e) {
            e.printStackTrace();
        }

    }
    public static byte[] encodeContent(String value){
        return value.getBytes();
    }
}

解析类:

    使用解析类解析出HTTP请求报文,并且得到 request信息和 response信息,就可以在 doGet()方法中写响应内容,然后通过Socket进行响应

package com.bittech.httpd.core;

import com.bittech.httpd.common.HttpRequest;
import com.bittech.httpd.common.HttpResponse;

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

/**
 * 此类专门用来解析
 */


public class HttpReqRespWrapper {
    private HttpRequest request;

    private HttpResponse response;

    private final Socket socket;

    public HttpReqRespWrapper(Socket socket) {
        this.socket = socket;
        this.parseHandler();
    }

    public HttpRequest request(){
        return request;
    }

    public HttpResponse response(){
        return response;
    }

    public void parseHandler(){
        Map<String,String> line = new HashMap<>();
        Map<String,String> header = new HashMap<>();
        Map<String, List<String>> params = new HashMap<>();
        try {
            InputStream inputStream = socket.getInputStream();
            // InputStream 是字节流,将其变为字符流
            InputStreamReader buffer = new InputStreamReader(inputStream);
            BufferedReader reader = new BufferedReader(buffer);

            //拿到首行
            String lineValue = reader.readLine();
            // 开始解析首行 GET /index?key1=value1&&key2=value2 HTTP/1.1
            //使用此类来进行首行参数分割
            StringTokenizer tokenizer = null;
            if(lineValue != null){
                tokenizer = new StringTokenizer(lineValue);
            }
            if(tokenizer !=null && tokenizer.hasMoreTokens()){
                String method = tokenizer.nextToken();
                line.put("method",method);
            }
            if(tokenizer != null && tokenizer.hasMoreTokens()){
                String url = tokenizer.nextToken();

                int index = url.indexOf("?");
                if(index != -1){
                    params = decodeParameters( url.substring(index+1));
                    line.put("url",url.substring(0,index));
                }else{
                    line.put("url",url);
                }
            }
            if(tokenizer != null && tokenizer.hasMoreTokens()){
                String version = tokenizer.nextToken();
                line.put("version",version);
            }
            //响应首行解析完毕

            // 第一行与第二行之间是个空行 \r\n
            lineValue = reader.readLine();
            if(lineValue !=null) {
                // 现在读取报头,由于报头中参数较多,需要反复去读  key : value
                while (!(lineValue.trim().isEmpty())) {
                    int index = lineValue.indexOf(":");
                    String key = lineValue.substring(0, index);
                    String value = lineValue.substring(index + 1);
                    header.put(key, value);
                    lineValue = reader.readLine();
                }
            }

            this.request = new DefaultHttpRequest(line,header,params);
            this.response = new DefaultHttpResponse(socket,request);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private static Map<String, List<String>> decodeParameters(String queryString) {
        Map<String, List<String>> params = new HashMap<>();

        if(queryString !=null){
            StringTokenizer tokenizer = new StringTokenizer(queryString,"&");
            while(tokenizer.hasMoreTokens()){
                String kv = tokenizer.nextToken();
                int index = kv.indexOf("=");
                String key = decodePercent(kv.substring(0,index));
                String value = decodePercent(kv.substring(index+1));
                if(params.containsKey(key)){
                    List<String> values = params.get(key);
                    values.add(value);
                }else{
                    List<String> values = new ArrayList<>();
                    values.add(value);
                    params.put(key,values);
                }
            }
        }
        return params;
    }
    // 解码
    private static String decodePercent(String str) {
        String decoded = null;
        try {
            decoded = URLDecoder.decode(str, "UTF-8");
        } catch (UnsupportedEncodingException ignored) {
        }
        return decoded;
    }
}

   

服务器:

           1.服务器建立Socket链接,绑定ip地址和端口号,由于每次只接受一个客户端请求效率太慢,使用 newFixedThreadPool() 线程池来实现多线程,每当有新客户端请求链接时就新建线程链接,然后服务器创建ServerSocket实例,监听客户端发来的请求

           2.使用accept()方法获取客户端socket,使用 DispatcherHandler 这个类,根据 HTTP协议请求报文的格式,使用解析类      HttpReqRespWrapper 将URL解析出来,如果是 " . ",就是静态页面,如果输入的url在服务器中不存在,则返回 “404” 无法找到,如果url存在,则根据HTTP协议响应报文的格式拼接出响应报文.

           3.这就得到了response信息,在doGet方法中写响应内容,通过socket响应,显示出 静态页面或者动态页面

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值