栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

【尚学堂】手写WebServer

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【尚学堂】手写WebServer


持续学习&持续更新中…

守破离


【尚学堂】手写WebServer
    • 预备—反射
        • 什么是反射(Reflect)
        • 获取Class的三种方式
        • 利用反射创建对象的两种方式
    • 预备—XML解析
        • 简单解析
        • 解析web.xml
        • 结合反射与XML配置文件
    • 预备—HTTP协议
        • Request—GET
        • Request—POST
        • Response
    • 手写WebServer(简易)
        • 封装Request
        • 封装Response
        • 封装Dispatcher
        • 封装Server
    • 参考

Web请求都是使用Reauest和Response式的交流

预备—反射 什么是反射(Reflect)

反射是框架设计的灵魂

获取Class的三种方式
		//三种方式
		//1、对象.getClass()
		Iphone iphone =new Iphone();
		Class clz = iphone.getClass();
		//2、类.class
		clz = Iphone.class;
		//3、Class.forName("包名.类名")
		clz = Class.forName("com.sxt.server.basic.Iphone");
利用反射创建对象的两种方式
		//创建对象
		// 方式一(不推荐)
		Iphone iphone1 =(Iphone)clz.newInstance();
		// 方式二(推荐)
		Iphone iphone2 =(Iphone)clz.getConstructor().newInstance();
预备—XML解析 简单解析

p.xml:



    
        至尊宝
        9000
    
    
        白晶晶
        7000
    


解析:

        //SAX解析
        //1、获取解析工厂
        SAXParserFactory factory = SAXParserFactory.newInstance();
        //2、从解析工厂获取解析器
        SAXParser parse = factory.newSAXParser();
        //3、编写处理器
        //4、加载文档 document 注册处理器
        PersonHandler handler = new PersonHandler();
        //5、解析
        parse.parse(Thread.currentThread().getContextClassLoader()
                        .getResourceAsStream("p.xml")
                , handler);
        final List persons = handler.getPersons();
        for (Person person : persons) {
            System.out.println(person);
        }

PersonHandler:

class PersonHandler extends DefaultHandler {

    private final List persons = new ArrayList<>();
    private Person person;
    private String currentTag;

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        if (qName.equals("person")) {
            person = new Person();
        }
        currentTag = qName;
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        if (qName.equals("person")) {
            persons.add(person);
        }
        currentTag = null;
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        String content = new String(ch, start, length).trim();
        if (currentTag != null) {
            if ("name".equals(currentTag)) {
                person.setName(content);
            } else if ("age".equals(currentTag)) {
                if (!"".equals(content)) {
                    person.setAge(Integer.valueOf(content));
                }
            }
        }
    }

    public List getPersons() {
        return persons;
    }

}
解析web.xml

web.xml:



    
        login
        programmer.lp.webserver.servlet.LoginServlet
    
    
        login
        /login

public class Entity {

    private String name;
    private String clz;

    public Entity() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getClz() {
        return clz;
    }

    public void setClz(String clz) {
        this.clz = clz;
    }
}

Mapping :

package programmer.lp.webserver.webconfig;



import java.util.HashSet;
import java.util.Set;

public class Mapping {

    private String name;
    private Set urlPatterns;

    public Mapping() {
        urlPatterns = new HashSet<>();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Set getUrlPatterns() {
        return urlPatterns;
    }

    public void setUrlPatterns(Set urlPatterns) {
        this.urlPatterns = urlPatterns;
    }

    public void addUrlPattern(String urlPattern) {
        urlPatterns.add(urlPattern);
    }

}

WebHandler :

package programmer.lp.webserver.webconfig;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import java.util.ArrayList;
import java.util.List;

public class WebHandler extends DefaultHandler {

    private final List entities = new ArrayList<>();
    private final List mappings = new ArrayList<>();
    private Entity entity;
    private Mapping mapping;
    private String currentTag;
    private boolean isMapping;

    public List getEntities() {
        return entities;
    }

    public List getMappings() {
        return mappings;
    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        if (qName.equals("servlet")) {
            entity = new Entity();
            isMapping = false;
        } else if (qName.equals("servlet-mapping")) {
            mapping = new Mapping();
            isMapping = true;
        }
        currentTag = qName;
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        if (qName.equals("servlet")) {
            entities.add(entity);
        } else if (qName.equals("servlet-mapping")) {
            mappings.add(mapping);
        }
        currentTag = null;
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        String content = new String(ch, start, length).trim();
        if (currentTag != null) {
            if (isMapping) {
                if ("servlet-name".equals(currentTag)) {
                    mapping.setName(content);
                } else if ("url-pattern".equals(currentTag)) {
                    mapping.addUrlPattern(content);
                }
            } else {
                if ("servlet-name".equals(currentTag)) {
                    entity.setName(content);
                } else if ("servlet-class".equals(currentTag)) {
                    entity.setClz(content);
                }
            }
        }
    }

}

WebContext :

package programmer.lp.webserver.webconfig;


import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class WebContext {

    private final Map entityMap = new HashMap<>();
    private final Map mappingMap = new HashMap<>();
    private final Map mappingALlMap = new HashMap<>();

    public WebContext(List entities, List mappings) {
        for (Entity entity : entities) {
            entityMap.put(entity.getName(), entity.getClz());
        }
        for (Mapping mapping : mappings) {
            final Set urlPatterns = mapping.getUrlPatterns();
            for (String urlPattern : urlPatterns) {
                if (urlPattern.endsWith("*")) {
                    urlPattern = urlPattern.substring(0, urlPattern.lastIndexOf("*;q=0.8
Accept-Language: zh-CN,zh;q=0.9
GET /?username=lp&password=1234 HTTP/1.1
Host: localhost:8888
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.9 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,**;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8

uname=lp&pwd=12
Response

响应格式:

HTTP/1.1 200 OK
Date: Tue, 16 Nov 2021 14:48:29 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 44543

响应正文
手写WebServer(简易)

以后有时间的话,再仔细研究研究。

封装Request
package programmer.lp.webserver.server;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.util.*;

public class Request {

    private static final String CRLF = "rn";

    private String requestInfo;
    private String method; // 小写
    private String uri; // 前面带/
    private String params = ""; // 浏览器地址栏可以写参数
    private Map> paramsMap;


    public Request(Socket client) throws IOException {
        this(client.getInputStream());
    }

    public Request(InputStream is) {
        paramsMap = new HashMap<>();
        try {
            byte[] buf = new byte[1024 * 1024];
            int len = is.read(buf);
            requestInfo = new String(buf, 0, len);
            parseRequest();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void parseRequest() {
        method = requestInfo.substring(0, requestInfo.indexOf(" /")).toLowerCase();
        uri = requestInfo.substring(requestInfo.indexOf("/"), requestInfo.indexOf(" HTTP/"));
        int indexOfParam = uri.indexOf("?");
        if (indexOfParam >= 0) {
            params += uri.substring(indexOfParam + 1);
            uri = uri.substring(0, indexOfParam);
        }
        if ("post".equals(method)) {
            String param = requestInfo.substring(requestInfo.lastIndexOf(CRLF)).trim();
            if (params.equals("")) {
                params += param;
            } else {
                params += "&";
                params += param;
            }
        }
//        System.out.println(method);
//        System.out.println(uri);
//        System.out.println(params);

        parseParams();

    }

    private void parseParams() {
        if (params.length() <= 0) return;
        final String[] keyValues = params.split("&");
        for (String keyValue : keyValues) {
            String[] kvArr = keyValue.split("=");
            kvArr = Arrays.copyOf(kvArr, 2);
            String key = kvArr[0];
            String value = kvArr[1];
            if (!paramsMap.containsKey(key)) {
                paramsMap.put(key, new ArrayList<>());
            }
            value = value == null ? "" : value;
            if (method.equals("get")) {
                value = decodeGetParam(value, "UTF-8");
            }
            paramsMap.get(key).add(value);
        }
    }

    private String decodeGetParam(String value, String code) {
        try {
            return java.net.URLDecoder.decode(value, code);
        } catch (UnsupportedEncodingException e) {
            return value;
        }
    }

    public String[] getParameters(String name) {
        if (!paramsMap.containsKey(name)) return null;
        return paramsMap.get(name).toArray(new String[]{});
    }

    public String getParameter(String name) {
        try {
            return getParameters(name)[0];
        } catch (Exception e) {
            return "";
        }
    }


    public String getMethod() {
        return method;
    }

    public String getUri() {
        return uri;
    }
    
}

封装Response
package programmer.lp.webserver.server;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Date;

public class Response {

    private static final String CRLF = "rn";
    private static final String BLANK = " ";

    private BufferedWriter writer;
    private final StringBuilder responseContent = new StringBuilder();
    private final StringBuilder responseHeaders = new StringBuilder();

    public Response(Socket client) throws IOException {
        this(client.getOutputStream());
    }

    public Response(OutputStream outputStream) {
        try {
            writer = new BufferedWriter(new OutputStreamWriter(outputStream));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Response print(String content) {
        responseContent.append(content);
        return this;
    }

    public Response println(String content) {
        responseContent.append(content).append(CRLF);
        return this;
    }

    public void createHeader(int code) {
        //1、响应行: HTTP/1.1 200 OK
        responseHeaders.append("HTTP/1.1").append(BLANK);
        responseHeaders.append(code).append(BLANK);
        String status = "OK";
        switch (code) {
            case 404:
                status = "NOT FOUND";
                break;
            case 500:
                status = "SERVER ERROR";
                break;
        }
        responseHeaders.append(status).append(CRLF);
        //2、响应头(最后一行存在空行):
        
        responseHeaders.append("Date: ").append(new Date()).append(CRLF);
        responseHeaders.append("Content-Type: text/html").append(CRLF);
        responseHeaders.append("Content-Length: ")
                .append(responseContent.toString().getBytes(StandardCharsets.UTF_8).length)
                .append(CRLF);
        //3、空行
        responseHeaders.append(CRLF);
    }

    public void pushToBrowser(int code) {
        // 响应正文
        try {
            createHeader(code);
            writer.append(responseHeaders.toString());
            writer.append(responseContent.toString());
            writer.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

封装Dispatcher
package programmer.lp.webserver.server;

import programmer.lp.webserver.servlet.Servlet;
import programmer.lp.webserver.webconfig.WebApp;

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

public class Dispatcher implements Runnable {

    private Socket client;
    private Request request;
    private Response response;

    public Dispatcher(Socket client) {
        this.client = client;
        try {
            request = new Request(client);
            response = new Response(client);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        try {
            final String requestUri = request.getUri();
//            if (requestUri.equals("") || requestUri.equals("/")) {
//                goIndex();
//                release();
//                return;
//            }
            final Servlet servlet = WebApp.getServletFromUri(requestUri);
            if (null != servlet) {
                servlet.service(request, response);
                response.pushToBrowser(200);
            } else {
                goError(404);
            }
        } catch (Exception e) {
            e.printStackTrace();
            goError(500);
        }
        release();
    }

    private void goError(int code) {
        // 读取error.html页面
        String info = readFromResource("error.html");
        info = info.replace("#error#", code + "");
        response.print(info);
        response.pushToBrowser(code);
    }

//    private void goIndex() {
//        // 读取index.html页面
//        String info = readFromResource("index.html");
//        response.print(info);
//        response.pushToBrowser(200);
//    }

    private String readFromResource(String fileName) {
        StringBuilder info = new StringBuilder();
        try (final InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName);) {
            int len;
            byte[] buf = new byte[1024];
            while ((len = resourceAsStream.read(buf)) != -1) {
                info.append(new String(buf, 0, len));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return info.toString();
    }

    private void release() {
        try {
            client.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

封装Server
package programmer.lp.webserver.server;

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

public class Server {

    private ServerSocket serverSocket;
    private boolean isRunning;

    //启动服务
    public void start() {
        try {
            serverSocket = new ServerSocket(8888);
            isRunning = true;
            receive();
        } catch (IOException e) {
            e.printStackTrace();
            stop();
        }
    }

    //接受连接处理
    public void receive() {
        while (isRunning) {
            try {
                Socket client = serverSocket.accept();
                new Thread(new Dispatcher(client)).start();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    //停止服务
    public void stop() {
        isRunning = false;
        try {
            serverSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Server server = new Server();
        server.start();
    }

}

参考

尚学堂: JAVA课程.


本文完,感谢您的关注支持!


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/532106.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号