【尚学堂】手写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、类.classclz = 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:
<?xml version="1.0" encoding="UTF-8" ?>
<persons><person><name>至尊宝</name><age>9000</age></person><person><name>白晶晶</name><age>7000</age></person>
</persons>
解析:
//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<Person> persons = handler.getPersons();for (Person person : persons) {System.out.println(person);}
PersonHandler:
class PersonHandler extends DefaultHandler {private final List<Person> persons = new ArrayList<>();private Person person;private String currentTag;@Overridepublic void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {if (qName.equals("person")) {person = new Person();}currentTag = qName;}@Overridepublic void endElement(String uri, String localName, String qName) throws SAXException {if (qName.equals("person")) {persons.add(person);}currentTag = null;}@Overridepublic 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<Person> getPersons() {return persons;}}
解析web.xml
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app><servlet><servlet-name>login</servlet-name><servlet-class>programmer.lp.webserver.servlet.LoginServlet</servlet-class></servlet><servlet-mapping><servlet-name>login</servlet-name><url-pattern>/login/*</url-pattern></servlet-mapping><servlet><servlet-name>register</servlet-name><servlet-class>programmer.lp.webserver.servlet.RegisterServlet</servlet-class></servlet><servlet-mapping><servlet-name>register</servlet-name><url-pattern>/register/*</url-pattern></servlet-mapping>
</web-app>
Entity :
package programmer.lp.webserver.webconfig;/*<servlet><servlet-name>login</servlet-name><servlet-class>programmer.lp.basic.servlet.LoginServlet</servlet-class></servlet>*/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;/*<servlet-mapping><servlet-name>login</servlet-name><url-pattern>/login</url-pattern><url-pattern>/g</url-pattern></servlet-mapping>*/import java.util.HashSet;
import java.util.Set;public class Mapping {private String name;private Set<String> urlPatterns;public Mapping() {urlPatterns = new HashSet<>();}public String getName() {return name;}public void setName(String name) {this.name = name;}public Set<String> getUrlPatterns() {return urlPatterns;}public void setUrlPatterns(Set<String> 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<Entity> entities = new ArrayList<>();private final List<Mapping> mappings = new ArrayList<>();private Entity entity;private Mapping mapping;private String currentTag;private boolean isMapping;public List<Entity> getEntities() {return entities;}public List<Mapping> getMappings() {return mappings;}@Overridepublic 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;}@Overridepublic 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;}@Overridepublic 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<String, String> entityMap = new HashMap<>();private final Map<String, String> mappingMap = new HashMap<>();private final Map<String, String> mappingALlMap = new HashMap<>();public WebContext(List<Entity> entities, List<Mapping> mappings) {for (Entity entity : entities) {entityMap.put(entity.getName(), entity.getClz());}for (Mapping mapping : mappings) {final Set<String> urlPatterns = mapping.getUrlPatterns();for (String urlPattern : urlPatterns) {if (urlPattern.endsWith("*")) {urlPattern = urlPattern.substring(0, urlPattern.lastIndexOf("/*"));mappingALlMap.put(urlPattern, mapping.getName());} else {mappingMap.put(urlPattern, mapping.getName());}}}}public String getServletClass(String urlPattern) {final Set<Map.Entry<String, String>> entries = mappingALlMap.entrySet();for (Map.Entry<String, String> entry : entries) {final String key = entry.getKey();if (urlPattern.startsWith(key)) {return entityMap.get(mappingALlMap.get(key));}}return entityMap.get(mappingMap.get(urlPattern));}}
结合反射与XML配置文件
package programmer.lp.webserver.webconfig;import programmer.lp.webserver.servlet.Servlet;import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;public class WebApp {private static WebContext webContext;static {try {//SAX解析//1、获取解析工厂SAXParserFactory factory = SAXParserFactory.newInstance();//2、从解析工厂获取解析器SAXParser parse = factory.newSAXParser();//3、编写处理器//4、加载文档 Document 注册处理器WebHandler handler = new WebHandler();//5、解析parse.parse(Thread.currentThread().getContextClassLoader().getResourceAsStream("web.xml"), handler);webContext = new WebContext(handler.getEntities(), handler.getMappings());} catch (Exception e) {e.printStackTrace();}}public static Servlet getServletFromUri(String uri) {// final String uri = "/register";final String servletClass = webContext.getServletClass(uri);try {return (Servlet) Class.forName(servletClass).getConstructor().newInstance();} catch (Exception e) {return null;}}}
预备—HTTP协议
HTTP协议是应用层协议,底层基于TCP协议。
Request—GET
GET / HTTP/1.1
Host: localhost:8888
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3878.400 QQBrowser/10.8.4518.400
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;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
Request—POST
Request方法是POST时,也可以在浏览器地址栏中加上请求参数,会将这些参数与请求BODY中的参数一块解析。
POST /index.html HTTP/1.1
Host: localhost:8888
Connection: keep-alive
Content-Length: 15
Cache-Control: max-age=0
Content-Type: application/x-www-form-urlencoded
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.8uname=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 = "\r\n";private String requestInfo;private String method; // 小写private String uri; // 前面带/private String params = ""; // 浏览器地址栏可以写参数private Map<String, List<String>> 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 = "\r\n";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 OKresponseHeaders.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、响应头(最后一行存在空行):/*Date: Mon,31Dec209904:25:57GMTContent-Type: text/htmlContent-Length: 39725426*/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();}}@Overridepublic 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课程.
本文完,感谢您的关注支持!
【尚学堂】手写WebServer相关推荐
- 手写webserver服务器
手写webserver服务器 文章目录 手写webserver服务器 前言 一.web server执行流程 组件说明 项目地址 二.代码实现 三. 效果展示 四.总结 前言 webserver 服务 ...
- 手写webserver
补充知识点 反射:把java类中的各种结构方法(方法.属性.构造器.类名)映射成一个个的java对象 1.获取Class对象 三种方式: 对象-getClass() Iphone iphone = n ...
- 尚学堂JAVA高级学习笔记_1/2
尚学堂JAVA高级学习笔记 文章目录 尚学堂JAVA高级学习笔记 写在前面 第1章 手写webserver 1. 灵魂反射 2. 高效解析xml 3. 解析webxml 4. 反射webxml 5. ...
- 汉仪尚巍手书有版权吗_汉仪尚巍手书字体下载 汉仪尚巍手书体W字体免费版下载...
手写书法字体小编已经发布过很多了,今天发布的这款汉仪尚巍手书字体是由汉仪字库出品的一套手写书法字体,比较完整,个人使用免费,如果用于商业用途需要联系汉仪官方获取授权,适用于标题.印刷.包装.电商使用. ...
- 【2020尚硅谷Java大厂面试题第三季 04】Redis 9种数据类型使用场景,分布式锁演变步骤,lua脚本,redis事务,Redisson,Redis内存占用,删除策略,内存淘汰策略,手写LRU
1.安装redis6.0.8 2023 02 02 为:redis-7.0.8.tar.gz 2.redis传统五大数据类型的落地应用 3.知道分布式锁吗?有哪些实现方案?你谈谈对redis分布式锁的 ...
- [原创 - 尚学堂科技 - 马士兵老师]
JAVA自学之路 一:学会选择 [转载请注明出处:http://www.bjsxt.com/zixue/zixuezhilu_1.html] 为了就业,不少同学参加各种各样的培训. 决心做软件的,大多 ...
- 尚学堂JAVA基础学习笔记_2/2
尚学堂JAVA基础学习笔记_2/2 文章目录 尚学堂JAVA基础学习笔记_2/2 写在前面 第10章 IO技术 1. IO入门 2. IO的API 3. 装饰流 4. IO实战 5. CommonsI ...
- [转]尚学堂科技 - 马士兵老师-JAVA自学之路
[原创 - 尚学堂科技 - 马士兵老师] JAVA自学之路 一:学会选择 [转载请注明出处:http://www.bjsxt.com/zixue/zixuezhilu_1.html] 为了就业,不少同 ...
- JAVA自学之路 [原创 - 尚学堂科技 - 马士兵老师]
(我觉得看了之后挺不错的所以分享一下) JAVA自学之路 一:学会选择 为了就业,不少同学参加各种各样的培训. 决心做软件的,大多数人选的是java,或是.net,也有一些选择了手机.嵌入式.游戏.3 ...
最新文章
- 剑指offer :从尾到头打印链表
- Spring MVC-09循序渐进之文件上传(基于Servlet3.0+Html5客户端上传文件)
- win10新建管理员账户_【经验篇001】Win10专业版如何开启超级管理员账户
- Duplicate entry...for key...
- js 单精度浮点数转10进制_js浮点数精度问题的前世今生?
- 汪文君Google Guava实战视频教程
- 电子电路绘图与仿真软件
- 10000字拆解:五个美妆新品牌私域背后的数据和逻辑
- 《千与千寻》告诉产品经理什么?
- 读心神探感悟 读心神探 语录 读心神探 观后感
- 最严“22条措施”打击市场乱象 云南旅游“浴火重生”
- 国内外几个主流的CMS系统推荐
- 电脑CPU使用率过高怎么办
- 最新多功能校园表白墙源码 LoveWall V2.0Pro
- 维盟智能路由_维盟智能WIFI路由器怎么设置?
- ESModule 系列 (二):构建下一代基础设施 PDN
- RLC串并联谐振回路特性、如何判断容性感性
- 迅为4418开发板Linux系统修改和固定MAC地址
- 作文 我眼中的计算机1000字,你眼中的我作文1000字
- linux系统weblogic10.3.6(jar) 下载安装