Java 11 仿Tomcat服务器及postman模拟发送

OYそ / 2023-08-11 / 原文

image

request
package tomcat;

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

//请求
public class Request {
    String method;//方式
    String uri;//路由
    String protocol;//协议

    Map<String,String> heads;//请求头
    String content;//请求体

    //区别于static{}  普通方法
    {
        heads = new HashMap<>();
    }

}
response
package tomcat;

import java.util.Map;

//响应
public class Response {
     String protocol; //协议
    int code; //状态码
    String status;//状态描述
    Map<String,String> heads;//头
    {
        heads = new HashMap<>();
    }
    String filename;//静态资源
                    //动态资源
    OutputStream out;//仿照真实对象
}

面向对象技术 HttpServer
package tomcat;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
//仿Tomcat服务器
public class HttpServer {
    public static void main(String[] args) {

        try {
            ServerSocket serverSocket = new ServerSocket(8080);//Tomcat默认绑定8080端口   127.0.0.1:8080
            while(true){//每次有客户端(浏览器)访问,就会产生一个Socket
              final Socket socket = serverSocket.accept();
                new Thread(() -> {//子线程  处理具体业务 new Thread(new Runnanber()   生成run方法,具体方法在run()里面,无法抛

                    try {//请求对象     //面向过程技术
                        byte[] buffer = new byte[1024];
                        InputStream is = socket.getInputStream();//一次读完
                        int len = is.read(buffer);
                        System.out.println("aaaaaaaaa"+len);
                        System.out.println(new String(buffer,0,len));
                            //响应对象
                        //固定回应一个<h1>  面向过程,弊端:过程内容多了就
                        OutputStream os = socket.getOutputStream();
                        os.write("HTTP/1.1 200 OK\r\n".getBytes());//请求行
                        os.write("Content-Type: text/html; charset=utf-8\r\n".getBytes());//请求头
                        os.write("\r\n\r\n".getBytes());
                        os.write("<h1>你好!世界</h1>".getBytes());//

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

                }).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println(e.getMessage());
        }
        System.out.println("服务器结束了!");
    }
}

面向对象技术 HttpServer2
package tomcat;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

//仿Tomcat服务器  面向对象技术
public class HttpServer2 {
    public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(8080);
            while(true){//每次有客户端(浏览器)访问,就会产生一个Socket
              final Socket socket = serverSocket.accept();
                new Thread(() -> {
                    try {//请求对象     //面向对象技术
                        Request request = parseRequest(socket);
                        Response response = parseResponse(socket,request);
                        sendBrower(request,response);   //发送东西给浏览器

                        socket.close();
                    } catch (IOException e) {
                       throw new RuntimeException(e);
                    }

                }).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println(e.getMessage());
        }
        System.out.println("服务器结束了!");
    }

    //3.响应给浏览器
    private static void sendBrower(Request request, Response response) {
OutputStream out = response.out;
        //输出响应行
        out.write((response.protocol + " " + response.code + " " + response.status +"\r\n").getBytes());
        //输出响应头

        Set<Map.Entry<String,String>> entries = response.heads.entrySet();

        Iterator<Map.Entry<String, String>> iterator = entries.iterator();
        while(iterator.hasNext()){
            Map.Entry<String, String> entry= iterator.next();
            out.write((entry.getKey()+": "+entry.getValue()+"\r\n").getBytes());
        }
/*
        for (Map.Entry<String, String> entry : entries) {
            out.write((entry.getKey()+": "+entry.getValue()+"\r\n").getBytes());
        }*/
        out.write("\r\n".getBytes());//输出隔行
        //out.write("<h1>首页</h1>".getBytes());一般不怎么写,所以删掉这行

       //输出响应体  响应文件首页
        if(response.filename !=null){
            InputStream fis = new FileInputStream(response.filename);
            //边读边写
            byte[] buf = new byte[1024];
            int len;
            while ((len= fis.read(buf))!= -1){
                out.write(buf,0,len);
            }
            fis.close();
        }

    }

    //2.解析响应对象
    private static Response parseResponse(Socket socket, Request request) {
         Response response = new Response();
        //解析响应行
        response.protocol= request.protocol;
        response.code=200;
        response.status="OK";
        //解析响应体
        if("/".equals(request.uri)) request.uri="/index.html";
        switch (request.uri){//动态路由有各个case管理,一般需要与数据库进行交流,才能得出结论
            case "/login"://登录事件
                break;
            case "/register": //注册事件
                break;
            //静态路由由各个default管理
            default:
                int pos = request.uri.lastIndexOf('.');
                if (pos != -1) {//如果找不到 =-1
                    String extension = request.uri.substring(pos);
                    if(".html".equals(extension)) response.heads.put("Content-Type", "text/html");
	            if(".ico".equals(extension)) response.heads.put("Content-Type", "image/x-icon");
                    if(".png".equals(extension)) response.heads.put("Content-Type", "image/png");

                }
                response.filename = request.uri.substring(1); // index.html
                break;
        }
        return response;
    }
    //1.解析请求对象
    private static Request parseRequest(Socket socket) throws IOException {
        Request request = new Request();
        byte[] buffer = new byte[1024*3];
        int len =socket.getInputStream().read(buffer);
        //底层的字节流打印  16进制
        String strRequest = bytes2Str(buffer,len);
        System.out.println(strRequest);
        //请求的完整字符串
        String strReq = new String(buffer,0,len);
        System.out.println(strReq);

        //解析请求行
        int iRN1st = strReq.indexOf("\r\n"); //第一个\r\n的位置截取
        String reqLine = strReq.substring(0, iRN1st);//请求行
        System.out.println(reqLine.length());
        String[] reqLineArr = reqLine.split(" ");
        request.method = reqLineArr[0];
        request.uri = reqLineArr[1];
        request.protocol= reqLineArr[2];
        //解析请求头
        int iRNRN1st = strReq.indexOf("\r\n\r\n");
        String reqHead = strReq.substring(iRN1st+2, iRNRN1st);//请求头 \r\n\r\n结尾
        String[] reqHeadArr = reqHead.split("\r\n\r\n");
        //遍历
        for (String reqHeadItem : reqHeadArr) {
            String[] reqHeadItemArr = reqHeadItem.split(": ");
            request.heads.put(reqHeadItemArr[0],reqHeadItemArr[1]);
        }

        //解析请求体
        request.content = strReq.substring(iRNRN1st+4);
        return request;
    }
    /*
     * 字节数组转字符串,配有右侧字符显示
     * eg: A8H 30H 41H -> "A8 30 41  .0A"
     */
    private static String bytes2Str(byte[] bts, int len) {
        StringBuffer stringBuffer = new StringBuffer();
        int line = 0;
        StringBuffer sb = new StringBuffer();
        for (int i=0; i< len; i++) {
            stringBuffer.append(byte2Hex(bts[i]) + " ");
            if (bts[i]>= 0x20 && bts[i] <= 0x7e) {
                sb.append((char)bts[i]);
            } else {
                sb.append('.');
            }
            line++;
            if (line == 8) {
                stringBuffer.append(" ");
            }
            if (line >= 16) {
                stringBuffer.append("  " + sb);
                sb = new StringBuffer();
                stringBuffer.append("\r\n");
                line = 0;
            }
        }
        if (sb.length() > 0) {
            stringBuffer.append("  " + sb);
        }
        return stringBuffer.toString();
    }
    /*
     * 字节转十六进制
     * eg: A8H -> "A8"
     */
    private static String byte2Hex(byte bt) {
        char[] hex = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
        int lo = bt & 0x0f;
        int hi = (bt >> 4) & 0x0f;
        return "" + hex[hi] + hex[lo];
    }
}

image

image

1. postman 发送get请求
请求体content=null,请求方法为 get,以键值对的形式附到url中发送参数。
image

image

2. 普通表单post请求
以键值对的形式发送表单数据,参数名和参数值之间用=号连接,多个参数之间用&符号连接,同时表单参数携带到了url中。
image
image

3. 文件上传表单post请求
每次发送请求需要重新传入文件信息,数据处理为一条消息,以标签为单元,用分隔符分开
image
image