手写WebServer笔记
WebServer简介
上网浏览网页,离不开服务器,客户请求页面,服务器响应内容,响应的内容是根据每个web请求来产生动态内容,其内部即启动多个线程来产生不同内容。这种请求响应式的交互,都是基于HTTP协议的。当然现在随着业务越来越多样化,web服务器变得复杂了,拥有了像缓存、安全和session管理这些附加功能。
反射Reflection
把java类中的各种结构(方法、属性、构造器、类名)映射成一个个Java对象。利用反射技术可以 对一个类进行解剖,反射是框架设计的灵魂。
三种方式获取Class对象:
对象.getClass()
类.class
Class.forName(“包名.类名")(推荐)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import java.lang.reflect.InvocationTargetException;
public class ReflectTest { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Class clz = new Iphon().getClass(); clz = Iphon.class; clz = Class.forName("com.zephon.webserver.webserver.Iphon");
Iphon iphon = (Iphon)clz.getConstructor().newInstance(); System.out.println(iphon); } }
class Iphon{ public Iphon(){} }
|
XML解析
XML:Extensible Markup Language,可扩展标记语言,作为数据的一种存储格式或用于存储软件的参数,程序解析此配置文件,就可以到达不修改代码就能更改程序的目的。
解析XML:采用SAX解析
1 2 3 4 5 6 7 8 9 10 11
| <?xml version="1.0" encoding="UTF-8"?> <persons> <person> <name>至尊宝</name> <age>9000</age> </person> <person> <name>紫霞仙子</name> <age>8999</age> </person> </persons>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class Person { private String name; private int age;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import java.io.IOException; import java.util.ArrayList; import java.util.List;
public class XMLTest01 { public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException { SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParser parse = factory.newSAXParser(); PHandler handler = new PHandler(); parse.parse(Thread.currentThread().getContextClassLoader().getResourceAsStream("com/zephon/webserver/webserver/p.xml"),handler); List<Person> l = handler.getPersons(); for(Person p:l) System.out.println(p.getName()+"--"+p.getAge()); } } class PHandler extends DefaultHandler { private List<Person> persons; private Person person; private String tag;
@Override public void startDocument() throws SAXException { persons = new ArrayList(); }
@Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if(qName!=null) tag = qName; if(qName.equals("person")) person = new Person(); } @Override public void characters(char[] ch, int start, int length) throws SAXException { super.characters(ch, start, length); String str = new String(ch,start,length); if(str.length()>0 && tag!=null){ if(tag.equals("name")) person.setName(str); else if(tag.equals("age")) person.setAge(Integer.valueOf(str)); } }
@Override public void endElement(String uri, String localName, String qName) throws SAXException { super.endElement(uri, localName, qName); if(qName.equals("person")) persons.add(person); tag = null; }
@Override public void endDocument() throws SAXException { super.endDocument(); }
public List getPersons() { return persons; } }
|
SAX在Serverlet中的应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <?xml version="1.0" encoding="UTF-8"?> <web-app> <servlet> <servlet-name>login</servlet-name> <servlet-class>com.zephon.webserver.servlet.LoginServlet</servlet-class> </servlet> <servlet> <servlet-name>reg</servlet-name> <servlet-class>com.zephon.webserver.servlet.RegisterServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>login</servlet-name> <url-pattern>/login</url-pattern> <url-pattern>/g</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>reg</servlet-name> <url-pattern>/reg</url-pattern> </servlet-mapping> </web-app>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class Entity { private String name; private String clz;
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; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public class Mapping { private String name; private Set<String> patterns; Mapping(){ patterns = new HashSet<>(); } public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Set<String> getPatterns() { return patterns; }
public void setPatterns(Set<String> patterns) { this.patterns = patterns; } public void addPattern(String pattern){ this.patterns.add(pattern); } }
|
1 2 3 4
| public interface Servlet { void service(); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class LoginServlet implements Servlet { @Override public void service() { System.out.println("LoginServlet"); } } public class RegisterServlet implements Servlet {
@Override public void service() { System.out.println("RegisterServlet"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public class WebContext { private List<Entity> entities = null; private List<Mapping> mappings = null;
private Map<String,String> entityMap = new HashMap<>(); private Map<String,String> mappingMap = new HashMap<>();
public WebContext(List<Entity> entities, List<Mapping> mappings) { this.entities = entities; this.mappings = mappings;
for(Entity e:entities){ entityMap.put(e.getName(),e.getClz()); } for(Mapping m:mappings){ for(String p:m.getPatterns()){ mappingMap.put(p,m.getName()); } } }
public String getClz(String pattern){ String name = mappingMap.get(pattern); return entityMap.get(name); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
| public class XMLTest01 { public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParser parse = factory.newSAXParser(); WebHandler handler = new WebHandler(); parse.parse(Thread.currentThread().getContextClassLoader().getResourceAsStream("com/zephon/webserver/servlet/web.xml"),handler);
WebContext context = new WebContext(handler.getEntities(),handler.getMappings()); String name = context.getClz("/reg"); Class clz = Class.forName(name); Servlet servlet = (Servlet) clz.getConstructor().newInstance(); servlet.service();
} }
class WebHandler extends DefaultHandler { private List<Entity> entities; private List<Mapping> mappings; private Entity entity; private Mapping mapping; private String tag; private boolean isMapping;
@Override public void startDocument() throws SAXException { entities = new ArrayList<>(); mappings = new ArrayList<>(); }
@Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if(qName!=null) tag = qName; if(qName.equals("servlet")) { entity = new Entity(); isMapping = false; } else if(qName.equals("servlet-mapping")) { mapping = new Mapping(); isMapping = true; }
} @Override public void characters(char[] ch, int start, int length) throws SAXException { super.characters(ch, start, length); String str = new String(ch,start,length); if(str.length()>0 && tag!=null){ if(isMapping){ if(tag.equals("servlet-name")) mapping.setName(str); else if(tag.equals("url-pattern")) mapping.addPattern(str); }else{ if(tag.equals("servlet-name")) entity.setName(str); else if(tag.equals("servlet-class")) entity.setClz(str); }
} }
@Override public void endElement(String uri, String localName, String qName) throws SAXException { super.endElement(uri, localName, qName); if(qName.equals("servlet")) entities.add(entity); else if(qName.equals("servlet-mapping")) mappings.add(mapping); tag = null; }
@Override public void endDocument() throws SAXException { super.endDocument(); }
public List<Entity> getEntities() { return entities; }
public List<Mapping> getMappings() { return mappings; } }
|
HTTP协议
基础
超文本传输协议(HTTP,Hyper Text Transfer Protocol)是互联网上应用最为广泛的一种网络协议,所有的WWW文件都必须遵守这个标准。
请求协议:
- 请求行:方法(GET/POST)、URI、协议/版本
- 请求头:(Request Header)
- 请求正文:
响应协议:
- 状态行:协议/版本 状态码 状态描述
- 响应头(Response Header)
- 响应正文:
典型GET的请求协议:
1、请求行
GET /index.html?name=test&pwd=123456 HTTP/1.1
2、请求体
Accept:text/html,application/xhtml+xml,* / *
Accept-Language:zh-CN
User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.81 Safari/537.36
Accept-Encoding:gzip,deflate
Host:localhost
Connection:Keep-Alive
3、请求正文
典型POST的请求协议:
1、请求行
POST /index.html HTTP/1.1
2、请求体
Accept:text/html,application/xhtml+xml,* / * Accept-Language:zh-CN
User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.81 Safari/537.36
Host:localhost
Connection:Keep-Alive
3、请求正文
name=test&pwd=123456
典型的响应协议:
1、状态行:HTTP/1.0 200 OK
2、请求头:
Date:Tue Feb 19 15:18:50 CST 2019
Server:zephon Server/0.0.1;charset=GBK
Content-type:text/html
Content-length:384
3、请求正文(注意与请求头之间有个空行)
xxxxxxxx
实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>登录</title> </head> <body> <form method="post" action="http://localhost:8888/index.html"> 用户名:<input type="text" name="uname" id="uname"/> 密码:<input type="password" name="pwd" id="pwd"/> <input type="submit" value="登录"/> </form> </body> </html>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
|
public class Server01 { private ServerSocket serverSocket; public static void main(String[] args){ Server01 server = new Server01(); server.start(); } public void start(){ try { serverSocket = new ServerSocket(8888); receive(); } catch (IOException e) { e.printStackTrace(); System.out.println("服务器启动失败"); } } public void receive(){ try { Socket client = serverSocket.accept(); System.out.println("一个客户端建立成功"); InputStream is = client.getInputStream(); byte[] datas = new byte[1024*1024]; int len = is.read(datas); String requestInfo = new String(datas,0,len); System.out.println(requestInfo); } catch (IOException e) { e.printStackTrace(); System.out.println("客户端错误"); } } public void stop(){
}
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
|
public class Server02 { private ServerSocket serverSocket; public static void main(String[] args){ Server02 server = new Server02(); server.start(); } public void start(){ try { serverSocket = new ServerSocket(8888); receive(); } catch (IOException e) { e.printStackTrace(); System.out.println("服务器启动失败"); } } public void receive(){ try { Socket client = serverSocket.accept(); System.out.println("一个客户端建立成功"); InputStream is = client.getInputStream(); byte[] datas = new byte[1024*1024]; int len = is.read(datas); String requestInfo = new String(datas,0,len); System.out.println(requestInfo);
StringBuilder content = new StringBuilder(); content.append("<html>"); content.append("<head>"); content.append("<title>"); content.append("服务器响应成功"); content.append("</title>"); content.append("</head>"); content.append("<body>"); content.append("终于回来了..."); content.append("</body>"); content.append("</html>"); int size = content.toString().getBytes().length; StringBuilder responseInfo = new StringBuilder(); String blank = " "; String CRLF = "\r\n"; responseInfo.append("HTTP/1.1").append(blank); responseInfo.append("200").append(blank); responseInfo.append("OK").append(CRLF);
responseInfo.append("Date:").append(new Date()).append(CRLF); responseInfo.append("Server:").append("zephon Server/0.0.1;charset=GBK").append(CRLF); responseInfo.append("Content-type:text/html").append(CRLF); responseInfo.append("Content-length:").append(size).append(CRLF); responseInfo.append(CRLF); responseInfo.append(content.toString());
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(client.getOutputStream())); bw.write(responseInfo.toString()); bw.flush();
} catch (IOException e) { e.printStackTrace(); System.out.println("客户端错误"); } } public void stop(){
}
}
|
Response封装:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
|
public class Response { private BufferedWriter bw; private StringBuilder content; private StringBuilder headInfo; private int len; private final String BLANK = " "; private final String CRLF = "\r\n";
private Response(){ content = new StringBuilder(); headInfo = new StringBuilder(); len = 0; } public Response(Socket client){ this(); try { bw = new BufferedWriter(new OutputStreamWriter(client.getOutputStream())); } catch (IOException e) { e.printStackTrace(); } } public Response(OutputStream os){ this(); bw = new BufferedWriter(new OutputStreamWriter(os)); }
public Response print(String info){ content.append(info); len+=info.getBytes().length; return this; }
public Response println(String info){ content.append(info).append(CRLF); len+=(info+CRLF).getBytes().length; return this; }
public void pushToBrowser(int code) throws IOException { if(null==headInfo){ code=505; } createHeadInfo(code); bw.append(headInfo); bw.append(content); bw.flush(); }
private void createHeadInfo(int code){ headInfo.append("HTTP/1.1").append(BLANK); headInfo.append(code).append(BLANK); switch (code){ case 200: headInfo.append("OK").append(CRLF); break; case 404: headInfo.append("NOT FOUND").append(CRLF); break; case 505: headInfo.append("SERVER ERROR").append(CRLF); break; }
headInfo.append("Date:").append(new Date()).append(CRLF); headInfo.append("Server:").append("zephon Server/0.0.1;charset=GBK").append(CRLF); headInfo.append("Content-type:text/html").append(CRLF); headInfo.append("Content-length:").append(len).append(CRLF); headInfo.append(CRLF);
} }
|
封装Request:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
|
public class Request2 { private String requestInfo; private String method; private String url; private String queryStr; private Map<String, List<String>> parameterMap;
private final String CRLF = "\r\n";
public Request2(Socket client) throws IOException { this(client.getInputStream()); }
public Request2(InputStream is){ parameterMap = new HashMap<>(); byte[] datas = new byte[1024*1024]; int len = 0; try { len = is.read(datas); this.requestInfo = new String(datas,0,len); } catch (IOException e) { e.printStackTrace(); return; } parseRequestInfo(); }
private void parseRequestInfo(){ System.out.println("---分解---"); this.method = this.requestInfo.substring(0,this.requestInfo.indexOf("/")).toLowerCase(); this.method.trim(); int idx1 = this.requestInfo.indexOf("/")+1; int endIdx = this.requestInfo.indexOf("HTTP/"); this.url = this.requestInfo.substring(idx1,endIdx); int queryIdx = this.url.indexOf("?"); if(queryIdx>=0){ String[] urlArray = this.url.split("\\?"); this.url = urlArray[0]; queryStr = urlArray[1]; } System.out.println(url); if(method.trim().equals("post")){ String qStr = this.requestInfo.substring(this.requestInfo.lastIndexOf(CRLF)).trim(); if(null==queryStr){ queryStr = qStr; }else{ queryStr +="&"+qStr; } } queryStr=null==queryStr?"":queryStr; System.out.println(method+"-->"+url+"-->"+queryStr); convertMap(); }
private void convertMap(){ String[] keyValues = this.queryStr.split("&"); for(String queryStr:keyValues){ String[] kv = queryStr.split("="); kv = Arrays.copyOf(kv,2); String key = kv[0]; String value = kv[1]==null?null:decode(kv[1],"utf-8"); if(!parameterMap.containsKey(key)){ parameterMap.put(key,new ArrayList<String>()); } parameterMap.get(key).add(value); } }
private String decode(String value,String enc){ try { return java.net.URLDecoder.decode(value,enc); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return null; }
public String[] getParameterValues(String key){ List<String> values = this.parameterMap.get(key); if(null==values||values.size()<1){ return null; } return values.toArray(new String[0]); }
public String getParameter(String key){ String[] values = getParameterValues(key); return values==null?null:values[0]; }
public String getMethod() { return method; }
public String getUrl() { return url; }
public String getQueryStr() { return queryStr; } }
|
封装Servlet:
1 2 3 4 5 6 7 8 9 10 11
|
public interface Servlet { void service(Request request,Response response); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
public class LoginServlet implements Servlet { @Override public void service(Request request, Response response) { response.print("<html>"); response.print("<head>"); response.print("<title>"); response.print("第一个Servlet"); response.print("</title>"); response.print("</head>"); response.print("<body>"); response.print("欢迎回来..."+request.getParameter("uname")); response.print("</body>"); response.print("</html>"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| public class Server06 { private ServerSocket serverSocket; public static void main(String[] args){ Server06 server = new Server06(); server.start(); } public void start(){ try { serverSocket = new ServerSocket(8888); receive(); } catch (IOException e) { e.printStackTrace(); System.out.println("服务器启动失败"); } } public void receive(){ try { Socket client = serverSocket.accept(); System.out.println("一个客户端建立成功"); Request request = new Request(client);
Response response = new Response(client); Servlet servlet = null; if(request.getUrl().equals("login")){ servlet = new LoginServlet(); }else{ } servlet.service(request,response); response.pushToBrowser(200);
} catch (IOException e) { e.printStackTrace(); System.out.println("客户端错误"); } } public void stop(){
}
}
|
多线程处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| import java.io.IOException; 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 (IOException e) { e.printStackTrace(); this.release(); } }
@Override public void run() { try { Servlet servlet = WebApp.getServletFromUrl(request.getUrl()); if (null != servlet) { servlet.service(request, response); response.pushToBrowser(200); } else { response.pushToBrowser(404); } }catch (Exception e){ try { response.pushToBrowser(500); } catch (IOException e1) { e1.printStackTrace(); } } release(); } private void release(){ try { client.close(); } catch (IOException e) { e.printStackTrace(); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
|
public class Server07 { private ServerSocket serverSocket; private boolean isRunning; public static void main(String[] args){ Server07 server = new Server07(); server.start(); } public void start(){ try { isRunning = true; serverSocket = new ServerSocket(8888); receive(); } catch (IOException e) { e.printStackTrace(); System.out.println("服务器启动失败"); stop(); } } public void receive(){ while (isRunning) { try{ Socket client = serverSocket.accept(); System.out.println("一个客户端建立成功"); new Thread(new Dispatcher(client)).start(); } catch(IOException e){ e.printStackTrace(); System.out.println("客户端错误"); } } } public void stop(){ isRunning = false; try { this.serverSocket.close(); System.out.println("服务器已停止"); } catch (IOException e) { e.printStackTrace(); } }
}
|