持续学习&持续更新中…
守破离
【尚学堂】手写WebServer
- 预备—反射
- 什么是反射(Reflect)
- 获取Class的三种方式
- 利用反射创建对象的两种方式
- 预备—XML解析
- 简单解析
- 解析web.xml
- 结合反射与XML配置文件
- 预备—HTTP协议
- Request—GET
- Request—POST
- Response
- 手写WebServer(简易)
- 封装Request
- 封装Response
- 封装Dispatcher
- 封装Server
- 参考
预备—反射 什么是反射(Reflect)Web请求都是使用Reauest和Response式的交流
反射是框架设计的灵魂
获取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=12Response
响应格式:
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课程.
本文完,感谢您的关注支持!



