手写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>
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>
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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>
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 }
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 }
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 }
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 }
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 }
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 }
以上是关于手写Tomcat服务器的主要内容,如果未能解决你的问题,请参考以下文章