手写Tomcat服务器

Posted bfcs

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手写Tomcat服务器相关的知识,希望对你有一定的参考价值。

预备知识

编写服务器用到的知识点

1) Socket 编程
2) html
3) HTTP 协议
4) 反射
5) XML 解析
6) 服务器编写

Socket编程

https://www.cnblogs.com/bfcs/p/10790130.html

 

HTML知识

HTML:HyperText Markup Language 超文本标记语言用于描述网页文档的一种标记语言

表单(form):与用户之间进行交互

  method:请求方式 get/post

     get 数据量小,安全性低,默认方式

     post 数据量大,安全性高
  action:请求的服务器路径

  id :(用户的的浏览器在文档里区分唯一性)前端区分唯一性,js 中

  name:名称,后端(服务器)区分唯一性,获取值,只要提交数据给后台(服务器)必须存在 name

技术图片
 1 <html>
 2     <head>
 3         <title>登陆界面</title>
 4     </head>
 5     <body>
 6         <form action="" method="post" >
 7             <p>用户名:<input type="text" name="username" id="name"/></p>
 8             <p>密码:<input type="password" name="password" id="pwd"/></p>
 9             <input type="submit" value="提交"/>
10         </form>
11     </body>
12 </html>
View Code

 

HTTP协议

协议

1) 应用层:HTTP、FTP、TELNET、SNMP、DNS
2) 传输层:TCP、UDP
3) 网络层:IP

HTTP 协议简介

HTTP:超文本传输协议,是网络应用层的协议,建立在 TCP/IP 协议基础上,HTTP 使用可靠的 TCP 连接,默认端口为 80。

用户打开 Web 浏览器(常见的 HTTP 客户端),输入 URL地址,就能接收到远程 HTTP 服务器端发送过来的网页,即HTTP 遵循请求(Request)/应答(Response)模型。

Web 浏览器向 Web 服务器发送请求,Web 服务器处理请求并返回适当的应答,所有 HTTP 连接都被构造成一套请求与应答。

HTTP 协议严格规定了 HTTP 请求和 HTTP 响应的数据格式

HTTP 请求格式

1) 请求方式、URI(统一资源定位符)、HTTP 协议/版本

2) 请求头 Request Header

  请求头包含许多有关客户端环境和请求正文的有用信息。例如,请求头可以声明浏览器所用的语言,请求正文的长度等。

3) 请求正文 Requet Content (只有在 post 方式才有)请求头和请求正文之间必须有符号行(回车符或行结束符),与请求头分开。这个行非常重要,它表示请求头已结束,接

下来的是请求正文。 通常 post 方式的数据存放于此,请求正文中可以包含客户提交的查询字符串等信息。在实际应用中,HTTP 请求正文可以包含更多的内容

技术图片

HTTP响应格式

1) HTTP 协议版本、状态代码、描述

2) 响应头(Response Head)

3) 响应正文(Respose Content)

 

Tomcat

是 SUN 公司推出的小型 Servlet/JSP 调试工具)的基础上发展起来的一个优秀的 Servlet 容器,Tomcat本身完全用 Java 语言编写

Tomcat 使用

1) 配置 Tomcat

  a) JAVA_HOME Java JDK 的根目录

  b) CATALINA_HOME Tomcat 根目录

2) 启动和关闭 Tomcat

  启动 Tomcat 服务器:startup.bat  本地主机8080端口

  关闭 Tomcat 服务器:shutdown.bat

3) 部署项目到服务器

  在 webapps 目录下新建目录存放.html 页面     访问页面

Tomcat 的运行原理

客户浏览器发出要求,访问特定的 Servlet 的请求。

1) Tomcat 服务器接收到客户请求并解析。

2) Tomcat 服 务 器 创 建 一 个 ServletRequest 对 象 , 在ServletRequest 对象中包含了客户请求信息及其他关于客户的信息,如请求头,请求正文,以及客户机的 IP 地址等。

3) Tomcat 服务器创建一个 ServletResponse 对象

4) Tomcat 服务器调用客户所请求的 Servlet 的 service 服务方法,并且把 ServletRequst 对象和 ServletResponse 对象做为参数传给该服务方法。

5) Servlet 从 ServletRequest 对象中可获取客户的请求信息。

6) Servlet 利用 ServletResponse 对象来生成响应结果。

7) Tomcat 服务器把 Servlet 生成的响应结果发送给客户。

技术图片

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 

 

 

手写服务器项目

1.搭建项目框架

技术图片

 

2.编写XML文档

技术图片
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <web-app>
 3     <servlet>
 4         <servlet-name>login</servlet-name>
 5         <serlvet-class>com.bjsxt.servlet.LoginServlet</serlvet-class>
 6     </servlet>
 7     <servlet-mapping>
 8         <serlvet-name>login</serlvet-name>
 9         <url-pattern>/login</url-pattern>
10         <url-pattern>/log</url-pattern>
11     </servlet-mapping>
12     <servlet>
13         <servlet-name>register</servlet-name>
14         <serlvet-class>com.bjsxt.servlet.RegisterServlet</serlvet-class>
15     </servlet>
16     <servlet-mapping>
17         <serlvet-name>register</serlvet-name>
18         <url-pattern>/reg</url-pattern>
19         <url-pattern>/register</url-pattern>
20         <url-pattern>/regis</url-pattern>
21     </servlet-mapping>
22     <servlet>
23         <servlet-name>favicon</servlet-name>
24         <serlvet-class>com.bjsxt.servlet.FaviconServlet</serlvet-class>
25     </servlet>
26     <servlet-mapping>
27         <serlvet-name>favicon</serlvet-name>
28         <url-pattern>/favicon.ico</url-pattern>
29         
30     </servlet-mapping>
31 </web-app>
View Code

3.编写 IOCloseUtil 类

技术图片
 1 import java.io.Closeable;
 2 import java.io.IOException;
 3 
 4 public class IOCloseUtil { //用于关闭所有流
 5     public static void closeAll(Closeable...close) { //可变参数
 6         for (Closeable closeable : close) {
 7             if(closeable != null) {
 8                 try {
 9                     closeable.close();
10                 } catch (IOException e) {
11                     // TODO 自动生成的 catch 块
12                     e.printStackTrace();
13                 }
14             }
15         }
16     }
17 }
View Code

4.DOM4J 解析 XML 配置文件

  1)Entity 实体类的编写

技术图片
 1 import java.io.File;
 2 import java.util.ArrayList;
 3 import java.util.Iterator;
 4 import java.util.List;
 5 
 6 import org.dom4j.Document;
 7 import org.dom4j.DocumentException;
 8 import org.dom4j.Element;
 9 import org.dom4j.io.SAXReader;
10 import org.omg.CORBA.PUBLIC_MEMBER;
11 
12 public class WebDom4j { //用于解析XML
13     private List<Entitty> entityList;//用于存储N多Entity,而每一个Entity都是servlet-name与servlet-class
14     private List<Mapping> mappingList;//用于存储N多Mapping,而每一个Mapping都是一个servlet-name与多个url-pattern
15     
16     //公有取值赋值方法
17     public List<Entitty> getEntityList() {
18         return entityList;
19     }
20     public void setEntityList(List<Entitty> entityList) {
21         this.entityList = entityList;
22     }
23     public List<Mapping> getMappingList() {
24         return mappingList;
25     }
26     public void setMappingList(List<Mapping> mappingList) {
27         this.mappingList = mappingList;
28     }
29     
30     //构造方法
31     public WebDom4j() {
32         entityList = new ArrayList<Entitty>();
33         mappingList = new ArrayList<Mapping>();
34     }
35     //获取Document对象的方法
36     public Document getDocument() { //Document英语翻译:文件;文档
37         //alt+shift+z包围异常快捷键
38         try {
39             //(1)创建SAXReader对象
40             SAXReader reader = new SAXReader();
41             //(2)调用read()方法
42             return reader.read(new File("src/WEB_INFO/web.xml"));
43         } catch (DocumentException e) {
44             // TODO 自动生成的 catch 块
45             e.printStackTrace();
46         }
47         return null;
48     }
49     //把获取到的Document对象解析
50     public void parse(Document doc) {
51         //(1)获取根元素
52         Element root = doc.getRootElement(); //web-app
53         //(2)解析servlet
54         for(Iterator<Element> ite = root.elementIterator("servlet"); ite.hasNext();) {
55             Element subElement = ite.next();//得到每一个servlet
56             //创建一个实体类
57             Entitty ent = new Entitty();//用于存储servlet-name与servlet-class
58             for(Iterator<Element> subite = subElement.elementIterator(); subite.hasNext();) {
59                 Element ele = subite.next(); //可能是servlet-name,也可能是servlet-class
60                 if("servlet-name".equals(ele.getName())) {
61                     ent.setName(ele.getText()); //给实体类中的name赋值
62                 }else if ("servlet-class".equals(ele.getName())) {
63                     ent.setClazz(ele.getText());
64                 }
65             }
66             //经过上面的循环后Entity有值了,把Entity添加到集合中
67             entityList.add(ent);
68         }
69         //解析servlet-mapping
70         for(Iterator<Element> ite = root.elementIterator("servlet-mapping"); ite.hasNext();) {
71             Element subEle = ite.next();//得到每一个servlet-mapping
72             //创建一个mapping类对象
73             Mapping map = new Mapping();
74             //解析servlet-mapping下的子元素
75             for(Iterator<Element>  subite = subEle.elementIterator(); subite.hasNext();) {
76                 Element ele = subite.next();//可能是servlet-name,也可能是url-pattern
77                 if("servlet-name".equals(ele.getName())) {
78                     map.setName(ele.getText());
79                 }else if("url-pattern".equals(ele.getName())){
80                     //获取集合对象,调用集合对象的添加方法,添加元素
81                     map.getUrlPattern().add(ele.getText());
82                 }
83             }
84             //mapping添加到集合中
85             mappingList.add(map);
86         }
87     }
88 }
View Code

  2)Mapping 实体类的编写

技术图片
 1 /**
 2  * <servlet-mapping>
 3         <servlet-name>login</servlet-name>
 4         <url-pattern>/login</url-pattern>
 5         <url-pattern>/log</url-pattern>
 6     </servlet-mapping>
 7  * @author CHB
 8  *
 9  */
10 
11 import java.util.ArrayList;
12 import java.util.List;
13 
14 public class Mapping { //映射关系  多个路径访问共享资源 servlet-name和url-pattern对应的实体类 多个资源与小名之间的关系
15     private String name;//servlet-name
16     private List<String> urlPattern;//url-pattern
17     
18     //公有取值赋值方法
19     public String getName() {
20         return name;
21     }
22     public void setName(String name) {
23         this.name = name;
24     }
25     public List<String> getUrlPattern() {
26         return urlPattern;
27     }
28     public void setUrlPattern(List<String> urlPattern) {
29         this.urlPattern = urlPattern;
30     }
31     //构造方法
32     public Mapping() {
33         urlPattern = new ArrayList<String>();
34     }
35     public Mapping(String name, List<String> urlPattern) {
36         super();
37         this.name = name;
38         this.urlPattern = urlPattern;
39     }
40 }
View Code

  3)解析 XML 文件,WebDom4j类的编写

  导入Dom4j的jar包:在项目下新建文件夹lib,把jar包复制进去,导入后右键jar包选择构建路径再选择添加至构建路径

技术图片
 1 import java.io.File;
 2 import java.util.ArrayList;
 3 import java.util.Iterator;
 4 import java.util.List;
 5 
 6 import org.dom4j.Document;
 7 import org.dom4j.DocumentException;
 8 import org.dom4j.Element;
 9 import org.dom4j.io.SAXReader;
10 import org.omg.CORBA.PUBLIC_MEMBER;
11 
12 public class WebDom4j { //用于解析XML
13     private List<Entitty> entityList;//用于存储N多Entity,而每一个Entity都是servlet-name与servlet-class
14     private List<Mapping> mappingList;//用于存储N多Mapping,而每一个Mapping都是一个servlet-name与多个url-pattern
15     
16     //公有取值赋值方法
17     public List<Entitty> getEntityList() {
18         return entityList;
19     }
20     public void setEntityList(List<Entitty> entityList) {
21         this.entityList = entityList;
22     }
23     public List<Mapping> getMappingList() {
24         return mappingList;
25     }
26     public void setMappingList(List<Mapping> mappingList) {
27         this.mappingList = mappingList;
28     }
29     
30     //构造方法
31     public WebDom4j() {
32         entityList = new ArrayList<Entitty>();
33         mappingList = new ArrayList<Mapping>();
34     }
35     //获取Document对象的方法
36     private Document getDocument() { //Document英语翻译:文件;文档
37         //alt+shift+z包围异常快捷键
38         try {
39             //(1)创建SAXReader对象
40             SAXReader reader = new SAXReader();
41             //(2)调用read()方法
42             return reader.read(new File("src/WEB_INFO/web.xml"));
43         } catch (DocumentException e) {
44             // TODO 自动生成的 catch 块
45             e.printStackTrace();
46         }
47         return null;
48     }
49     //把获取到的Document对象解析
50     public void parse(Document doc) {
51         //(1)获取根元素
52         Element root = doc.getRootElement(); //web-app
53         //(2)解析servlet
54         for(Iterator<Element> ite = root.elementIterator("servlet"); ite.hasNext();) {
55             Element subElement = ite.next();//得到每一个servlet
56             //创建一个实体类
57             Entitty ent = new Entitty();//用于存储servlet-name与servlet-class
58             for(Iterator<Element> subite = subElement.elementIterator(); subite.hasNext();) {
59                 Element ele = subite.next(); //可能是servlet-name,也可能是servlet-class
60                 if("servlet-name".equals(ele.getName())) {
61                     ent.setName(ele.getText()); //给实体类中的name赋值
62                 }else if ("servlet-class".equals(ele.getName())) {
63                     ent.setClazz(ele.getText());
64                 }
65             }
66             //经过上面的循环后Entity有值了,把Entity添加到集合中
67             entityList.add(ent);
68         }
69         //解析servlet-mapping
70         for(Iterator<Element> ite = root.elementIterator("servlet-mapping"); ite.hasNext();) {
71             Element subEle = ite.next();//得到每一个servlet-mapping
72             //创建一个mapping类对象
73             Mapping map = new Mapping();
74             //解析servlet-mapping下的子元素
75             for(Iterator<Element>  subite = subEle.elementIterator(); subite.hasNext();) {
76                 Element ele = subite.next();//可能是servlet-name,也可能是url-pattern
77                 if("servlet-name".equals(ele.getName())) {
78                     map.setName(ele.getText());
79                 }else if("url-pattern".equals(ele.getName())){
80                     //获取集合对象,调用集合对象的添加方法,添加元素
81                     map.getUrlPattern().add(ele.getText());
82                 }
83             }
84             //mapping添加到集合中
85             mappingList.add(map);
86         }
87     }
88 }
View Code

 5.反射创建servlet对象

  1)编写 ServletContext 类:Servlet 上下文,就是一个容器,用于存储映射关系

技术图片
 1 import java.util.HashMap;
 2 import java.util.Map;
 3 
 4 public class ServletContext { //上下文 Entity与Mapping的映射关系 实体与映射关系类
 5     private Map<String, String> servlet;//key是servlet-name,值是servlet-class
 6     private Map<String, String> mapping;//hashmap键不能重复,值却可以,key是url-pattern, 值是servlet-name
 7     
 8     //公有的取值赋值方法
 9     public Map<String, String> getServlet() {
10         return servlet;
11     }
12     public void setServlet(Map<String, String> servlet) {
13         this.servlet = servlet;
14     }
15     public Map<String, String> getMapping() {
16         return mapping;
17     }
18     public void setMapping(Map<String, String> mapping) {
19         this.mapping = mapping;
20     }
21     
22     //构造方法
23     public ServletContext() {
24         servlet = new HashMap<String, String>();
25         mapping = new HashMap<String, String>();
26     }    
27 }
View Code

  2)编写 WebApp 类

  a) 初始化程序运行的数据

  b) 根据不同的 url 创建所请求的 Servlet 对象

技术图片
 1 import java.util.List;
 2 import java.util.Map;
 3 
 4 import javax.print.attribute.standard.Severity;
 5 
 6 import cn.chb.servlet.Servlet;
 7 
 8 /*    a) 初始化程序运行的数据
 9   b) 根据不同的 url 创建所请求的 Servlet 对象
10  * */
11 
12 public class WebApp { //app应用程序
13     private static ServletContext context;
14     static {//静态初始化代码块
15         context =  new ServletContext();
16         //分别获取对应关系的Map集合
17         Map<String, String> servlet = context.getServlet();
18         Map<String, String> mapping = context.getMapping();
19         //解析XML文件对象
20         WebDom4j web = new WebDom4j();
21         web.parse(web.getDocument());//解析XML并把数据放到了entityList和mappingList当中
22         //获取解析XML之后的List集合
23         List<Entitty> entityList = web.getEntityList();
24         List<Mapping> mappingList = web.getMappingList();
25         
26         //将List集合中的数据存储到Map集合
27         for(Entitty entity:entityList) {
28             servlet.put(entity.getName(), entity.getClazz());
29         }
30         for(Mapping map:mappingList) {
31             //遍历url-pattern集合
32             List<String> urlPattern = map.getUrlPattern();
33             for(String s:urlPattern) {
34                 mapping.put(s, map.getName());
35             }
36         }
37     }
38     /**
39      * 根据url创建不同的servlet对象
40      * @param url
41      * @return
42      * 
43      */
44     public static Servlet getServlet(String url){
45         if(url == null||url.trim().equals("")) {
46             return null;
47         }
48         try {
49             //如果url正确
50             String servletName = context.getMapping().get(url);//根据key(url)获取值(servlet-name)
51             //根据servlet-name得到对应的servlet-class
52             String servletClass = context.getServlet().get(servletName);//等到的是一个完整的包名+类名字符串
53             //使用反射创建servlet对象
54             Class<?> clazz = Class.forName(servletClass);
55             //调用无参构造方法创建servlet对象
56             Servlet servlet = (Servlet)clazz.newInstance();
57             return servlet;
58         } catch (ClassNotFoundException e) {
59             // TODO 自动生成的 catch 块
60             e.printStackTrace();
61         } catch (InstantiationException e) {
62             // TODO 自动生成的 catch 块
63             e.printStackTrace();
64         } catch (IllegalAccessException e) {
65             // TODO 自动生成的 catch 块
66             e.printStackTrace();
67         }
68         return null;
69     }
70 }
View Code

6.封装 Request_method_url

  1) 编写 Server: 启动服务   关闭服务

技术图片
 1 import java.io.BufferedWriter;
 2 import java.io.IOException;
 3 import java.io.InputStream;
 4 import java.io.OutputStreamWriter;
 5 import java.net.ServerSocket;
 6 import java.net.Socket;
 7 
 8 import com.bjsxt.servlet.Servlet;
 9 import com.bjsxt.util.IOCloseUtil;
10 
11 public class Server {//服务器,用于启动和停止服务
12     private ServerSocket server;
13     private boolean isShutDown=false;//默认没有出错
14     public static void main(String[] args) {
15         Server server=new Server();//创建服务器对象
16         server.start();
17     }
18     public void start(){
19         this.start(8888);
20     }
21     public void start(int port){
22         try {
23             server=new ServerSocket(port);
24             this.receive(); //调用接收请求信息的方法
25         } catch (IOException e) {
26             isShutDown=true;
27         }
28     }
29     private void receive() {
30         try {
31             while(!isShutDown){
32                 //(1)监听
33                 Socket client=server.accept();
34                 //创建线程类的对象
35                 Dispatcher dis=new Dispatcher(client);
36                 //创建线程的代理类,并启动线程
37                 new Thread(dis).start();
38             }    
39             
40         } catch (IOException e) {
41             this.stop();//关闭服务器
42         }
43         
44     }
45     public void stop(){
46         isShutDown=true;
47         IOCloseUtil.closeAll(server);
48     }
49 }
View Code

  2)编写 HTML

技术图片
 1 <html>
 2     <head>
 3         <title>登陆</title>
 4     </head>
 5     <body>
 6         <form action="http://127.0.1:8888/log" method="get" >
 7             <p>用户名:<input type="text" name="username" id="username"/></p>
 8             <p>密码:<input type="password" name="pwd" id="password"/></p>
 9             <p>
10                 爱好:<input type="checkbox" name="hobby" value="ball"/>足球
11                      <input type="checkbox" name="hobby" value="read"/>读书
12                      <input type="checkbox" name="hobby" value="pain"/>画画
13             </p>
14             <p><input type="submit" value="登陆"/></p>
15             <input type="submit" value="提交"/>
16         </form>
17     </body>
18 </html>
View Code

  3) 封装 Request_method_url

技术图片
  1 import java.io.InputStream;
  2 import java.io.UnsupportedEncodingException;
  3 import java.net.URLDecoder;
  4 import java.util.ArrayList;
  5 import java.util.Arrays;
  6 import java.util.HashMap;
  7 import java.util.List;
  8 import java.util.Map;
  9 
 10 public class Request { /*请求类*/
 11     private InputStream is;//输入流
 12     private String requestInfo;//请求字符串:请求方式,路径,参数,协议/协议版本,请求正文
 13     private String method;//请求方式
 14     private String url;//请求的url
 15     
 16     //输入框中的name为key,值为输入的内容
 17     /*
 18      * key:username  value:chb
 19      * key:pwd         value:123456
 20      */
 21     private Map<String, List<String>> parametermapValues;//参数
 22     private static final String CRLF="\\r\\n";//换行
 23     private static final String BLANK=" ";//空格
 24     //构造方法,初始化属性
 25     public Request() {
 26         parametermapValues = new HashMap<String, List<String>>();
 27         method = "";
 28         requestInfo = "";
 29         url = "";
 30     }
 31     public Request(InputStream is) {
 32         this();
 33         this.is = is;
 34         try {
 35             byte [] buf = new byte [20480];
 36             int len = this.is.read(buf);
 37             requestInfo = new String(buf, 0, len);
 38         } catch (Exception e) {
 39             // TODO 自动生成的 catch 块
 40             e.printStackTrace();
 41         }
 42         //调用本类中分解请求信息的方法
 43         this.parseRequestInfo();
 44     }
 45     //分解请求信息的方法 方式、路径、参数
 46     private void parseRequestInfo() {
 47         String paraString ="";//用于存储请求参数
 48         //获取请求参数的第一行
 49         String firstLine=requestInfo.substring(0, requestInfo.indexOf(CRLF)).trim();//从0开始到第一个换行
 50         //分解出请求方式
 51         int index = firstLine.indexOf("/");//找出斜线的位置GET /(这里) HTTP/1.1
 52         this.method = firstLine.substring(0, index).trim();//trim()去掉空格
 53         //分解url,可能包含参数,也可能不包含参数
 54         String urlString = firstLine.substring(index, firstLine.indexOf("HTTP/")).trim();
 55         //判断请求方式是GET还是POST
 56         if("get".equalsIgnoreCase(this.method)) {//GET包含请求参数
 57             if(urlString.contains("?")) {//包含有问号,说明有参数
 58                 String [] urlArray = urlString.split("\\\\?");//以?号分割获取参数
 59                 this.url = urlArray[0];
 60                 paraString = urlArray[1];
 61             }else {
 62                 this.url = urlString;
 63             }
 64         }else {//POST不包含请求参数,参数在请求正文
 65             this.url = urlString;
 66             //最后一个换行到结尾是请求正文
 67             paraString = requestInfo.substring(requestInfo.lastIndexOf(CRLF)).trim();
 68         }
 69         if(paraString.equals("")) {//如果没有参数
 70             return;
 71         }
 72     }
 73     //username=chbt&pwd=123&hobby=ball&hobby=paint
 74         /**
 75          * username=chb
 76          * pwd=123
 77          * hobby=ball
 78          * hobby=paint
 79          * 
 80          * username=
 81          * @param prarString
 82          */
 83         private void parseParam(String prarString){
 84             String [] token=prarString.split("&");
 85             for(int i=0;i<token.length;i++){
 86                 String keyValues=token[i];
 87                 String []keyValue=keyValues.split("=");  //username   chb   pwd   123
 88                 if (keyValue.length==1) {  //username=
 89                     keyValue=Arrays.copyOf(keyValue, 2);
 90                     keyValue[1]=null;
 91                 }
 92                 //将  表单元素的name与name对应的值存储到Map集合
 93                 String key=keyValue[0].trim();
 94                 String value=keyValue[1]==null?null:decode(keyValue[1].trim(), "utf-8");
 95                 //放到集合中存储
 96                 if (!parametermapValues.containsKey(key)) {
 97                     parametermapValues.put(key, new ArrayList<String>());
 98                 }
 99                 List<String> values=parametermapValues.get(key);
100                 values.add(value);
101             }
102         }
103         //根据表单元素的name获取多个值
104         private String [] getParamterValues(String name){
105             //根据key获取value
106             List<String> values=parametermapValues.get(name);
107             if (values==null) {
108                 return null;
109             }else{
110                 return values.toArray(new String [0] );
111             }
112             
113         }
114         private String getParamter(String name){
115             //调用本类中根据name获取多个值的方法
116             String [] values=this.getParamterValues(name);
117             if (values==null) {
118                 return null;
119             }else{
120                 return values[0];
121             }
122         }
123         
124         //处理中文,因类浏览器对中文进行了编码,进行解码
125         private String decode(String value,String code){
126             try {
127                 return URLDecoder.decode(value, code);
128             } catch (UnsupportedEncodingException e) {
129                 // TODO Auto-generated catch block
130                 e.printStackTrace();
131             }
132             return null;
133         }
134 }
View Code

7.封装 Response

1) 构造响应头

2) 推送到客户端

技术图片
  1 import java.io.BufferedWriter;
  2 import java.io.IOException;
  3 import java.io.OutputStream;
  4 import java.io.OutputStreamWriter;
  5 import java.io.UnsupportedEncodingException;
  6 
  7 import com.bjsxt.util.IOCloseUtil;
  8 
  9 public class Response {//响应
 10     private StringBuilder headInfo;//响应头
 11     private StringBuilder content;//响应内容
 12     private int length;//响应内容的长度
 13     //
 14     private BufferedWriter bw;
 15     
 16     //两个常量,换行和空格
 17     private static final String CRLF="\\r\\n";//换行
 18     private static final String BLANK=" ";//空格
 19     
 20     //构造方法
 21     public Response() {
 22         headInfo=new StringBuilder();
 23         content=new StringBuilder();
 24         
 25     }
 26     //带参构造方法
 27     public Response(OutputStream os){
 28         this();//调用本类的无参构造方法
 29         try {
 30             bw=new BufferedWriter(new OutputStreamWriter(os, "utf-8"));
 31         } catch (UnsupportedEncodingException e) {
 32             headInfo=null;
 33         }
 34         
 35     }
 36     //构造正文部分
 37     public Response print(String info){
 38         content.append(info);
 39         try {
 40             length+=info.getBytes("utf-8").length;
 41         } catch (UnsupportedEncodingException e) {
 42             // TODO Auto-generated catch block
 43             e.printStackTrace();
 44         }
 45         return this;
 46     }
 47     public Response println(String info){
 48         content.append(info).append(CRLF);
 49         try {
 50             length+=(info+CRLF).getBytes("utf-8").length;
 51         } catch (UnsupportedEncodingException e) {
 52             // TODO Auto-generated catch block
 53             e.printStackTrace();
 54         }
 55         return this;
 56     }
 57     
 58     //构造响应头
 59     
 60     private void createHeadInfo(int code){
 61         headInfo.append("HTTP/1.1").append(BLANK).append(code).append(BLANK);
 62         switch (code) {
 63         case 200:
 64             headInfo.append("OK");
 65             break;
 66         case 500:
 67             headInfo.append("SERVER ERROR");
 68             break;
 69         default:
 70             headInfo.append("NOT FOUND");
 71             break;
 72         }
 73         headInfo.append(CRLF);
 74         headInfo.append("Content-Type:text/html;charset=utf-8").append(CRLF);
 75         headInfo.append("Content-Length:"+length).append(CRLF);
 76         headInfo.append(CRLF);
 77     }
 78     /**
 79      * 推送到客户机的浏览器
 80      * @param code
 81      */
 82     public void pushToClient(int code){
 83         if (headInfo==null) {
 84             code=500;
 85         }
 86         try {
 87             //调用本类中的构造响应头
 88             this.createHeadInfo(code);
 89             bw.write(headInfo.toString());
 90             bw.write(content.toString());
 91             bw.flush();
 92             this.close();
 93         } catch (IOException e) {
 94             // TODO Auto-generated catch block
 95             e.printStackTrace();
 96         }
 97     }
 98     public void close(){
 99         IOCloseUtil.closeAll(bw);
100     }
101 }
View Code

3)编写相应的 Servlet 构造响应内容

技术图片
 1 import com.bjsxt.server.Request;
 2 import com.bjsxt.server.Response;
 3 
 4 public abstract class Servlet { //是所有的请求的Servlet的父类
 5     public void service(Request req,Response rep) throws Exception{
 6         this.doGet( req, rep);
 7         this.doPost( req, rep);
 8     }
 9     public abstract void doGet(Request req,Response rep) throws Exception;
10     public abstract void doPost(Request req,Response rep) throws Exception;
11 }
View Code
技术图片
 1 import com.bjsxt.server.Request;
 2 import com.bjsxt.server.Response;
 3 
 4 public class LoginServlet extends Servlet {
 5 
 6     @Override
 7     public void doGet(Request req, Response rep) throws Exception {
 8             //获取请求参数
 9         String name=req.getParameter("username");
10         String pwd=req.getParameter("pwd");
11         
12         if(this.login(name, pwd)){
13             //调用响应中的构建内容的方
14             rep.println(name+"登录成功");
15         }else{
16             rep.println(name+"登录失败,对不起,账号或密码不正确");
17         }
18         
19     }
20     private boolean login(String name,String pwd){
21         if ("bjsxt".equals(name)&&"123".equals(pwd)) {
22             return true;
23         }
24         return false;
25     }
26 
27     @Override
28     public void doPost(Request req, Response rep) throws Exception {
29         // TODO Auto-generated method stub
30         
31     }
32 }
View Code
技术图片
 1 import com.bjsxt.server.Request;
 2 import com.bjsxt.server.Response;
 3 
 4 public class FaviconServlet extends Servlet {
 5 
 6     @Override
 7     public void doGet(Request req, Response rep) throws Exception {
 8         // TODO Auto-generated method stub
 9         
10     }
11 
12     @Override
13     public void doPost(Request req, Response rep) throws Exception {
14         // TODO Auto-generated method stub
15         
16     }
17 
18 }
View Code

8.封装分发器实现多线程

技术图片
 1 import java.io.IOException;
 2 import java.net.Socket;
 3 
 4 import com.bjsxt.servlet.Servlet;
 5 import com.bjsxt.util.IOCloseUtil;
 6 
 7 /**
 8  * 一个请求与响应就是一个Dispatcher
 9  * @author Administrator
10  *
11  */
12 public class Dispatcher implements Runnable {
13     private Request req;
14     private Response rep;
15     private Socket client;
16     private int code=200;//状态码
17     //构造方法初始化属性
18     public Dispatcher(Socket client) {
19         //将局部变量的值赋给成员变量
20         this.client=client;
21         try {
22             req=new Request(this.client.getInputStream());
23             rep=new Response(this.client.getOutputStream());
24         } catch (IOException e) {
25             code=500;
26             return ;
27         }
28     }
29     @Override
30     public void run() {
31         //根据不同的url创建指定的Servlet对象
32         //System.out.println(req.getUrl());
33         Servlet servlet=WebApp.getServlet(req.getUrl());
34         if (servlet==null) {
35             this.code=404;
36         }else{
37             //调用相应的Servlet中的service方法
38             try {
39                 servlet.service(req,rep);
40             } catch (Exception e) {
41                 this.code=500;
42             }
43         }
44         //将响应结果推送到客户机的浏览器
45         rep.pushToClient(code);
46         IOCloseUtil.closeAll(client);
47     }
48 
49 }
View Code

 

以上是关于手写Tomcat服务器的主要内容,如果未能解决你的问题,请参考以下文章

手写迷你版 Tomcat - Minicat

手写数字识别——基于全连接层和MNIST数据集

前端面试题之手写promise

从零开始手写Tomcat的教程4节---Tomcat默认连接器

手写Tomcat服务器

从零开始手写Tomcat的教程14节----服务器组件(Server)和服务组件(Service)