Java—手写服务器

1.预备知识_Socket知识回顾

1.1编写服务器用到的知识点

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

1.2复习Socket编程

1)C/S结构:客户端与服务器端一次双向通信
2)B/S结构:浏览器与服务器

httpClient_1

Client.java

package com.bjsxt.test;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;

public class Client {
	public static void main(String[] args) {
		//(1)创建Socket对象
		Socket client=null;
		//(2)获取输出流-->请求
		DataOutputStream dos=null;
		DataInputStream dis=null;
		try {
			client = new Socket("localhost", 8888);
			dos = new DataOutputStream(client.getOutputStream());
			dos.writeUTF("我是客户端:服务器你好!");
			//(3)获取输入流-->响应
			
			dis = new DataInputStream(client.getInputStream());
			System.out.println(dis.readUTF());
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
		//(4)关闭流
			IOClose.closeAll(dis,dos,client);
		}
	}
}

IOClose.java

package com.bjsxt.test;

import java.io.Closeable;
import java.io.IOException;

public class IOClose {
	//关闭全部的工具类
	public static void closeAll(Closeable...c){
		for (Closeable closeable : c) {
			if (closeable!=null) {
				try {
					closeable.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}

httpServer_1

Server.java

package com.bjsxt.server;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
	public static void main(String[] args) {
		//(1)创建ServerSocket对象
		ServerSocket server=null;
		//(2)监听是否有客户端发送请求
		Socket client=null;
		//(3)获取Scoket对象
				//(4)获取输入流  -->得到客户端的请求
		DataInputStream dis=null;
		DataOutputStream dos=null;
		try {
			server = new ServerSocket(8888);
			client = server.accept();
			dis = new DataInputStream(client.getInputStream());
			System.out.println(dis.readUTF());
			//(5)获取输出流-->给客户端响应结果
			
			dos = new DataOutputStream(client.getOutputStream());
			dos.writeUTF("客户端您好:我是服务器,我收到了你的信息");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
		//(6)关闭流
			IOClose.closeAll(dos,dis,client,server);
		}
		
	}
}

IOClose.java

package com.bjsxt.server;

import java.io.Closeable;
import java.io.IOException;

public class IOClose {
	//关闭全部的工具类
	public static void closeAll(Closeable...c){
		for (Closeable closeable : c) {
			if (closeable!=null) {
				try {
					closeable.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}

Server2.java

package com.bjsxt.server;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class Server2 {
	public static void main(String[] args) {
		//(1)创建ServerSocket对象
		ServerSocket server=null;
		//(2)监听是否有客户端发送请求
		Socket client=null;
		BufferedReader br=null;
		
		try {
			server = new ServerSocket(8888);
			client = server.accept();
			//获取来自浏览器的请求信息
			br=new BufferedReader(new InputStreamReader(client.getInputStream(), "utf-8"));
			String str=null;
			while((str=br.readLine()).length()>0){
				System.out.println(str);
			}
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
		//(6)关闭流
			IOClose.closeAll(br,client,server);
		}
		
	}
}

2. 预备知识_HTML 简单入门

2.1HTML

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

2.2 第一个 HTML 文档

<html>
	<head>
	<title>第一个 HTML</title>
	</head>
	<body>
	<h1>hello world</h1>
	</body>
</html>

2.3 表单 form

作用:与用户之间进行交互
method:请求方式 get/post
get 数据量小,安全性低,默认方式
post 数据量大,安全性高
action:请求的服务器路径

<form action="http://localhost:8888/index.html" method="post">
	<p>用户名:
	<input type="text" name="uname" id="name"/>
	</p>
	<p>密码 :
	<input type="password" name="pwd" id="pass"/>
	</p>
	<p><input type="submit" value="登录" /> </p>
</form>

id : (用户的的浏览器在文档里区分唯一性)前端区分唯一性,js 中
name:名称,后端(服务器)区分唯一性,获取值,只要提交数据
给后台(服务器)必须存在 name

httpServer_2

Server.java

package com.bjsxt.server;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
	public static void main(String[] args) {
		//(1)创建ServerSocket对象
		ServerSocket server=null;
		//(2)监听是否有客户端发送请求
		Socket client=null;
		//(3)获取Scoket对象
				//(4)获取输入流  -->得到客户端的请求
		DataInputStream dis=null;
		DataOutputStream dos=null;
		try {
			server = new ServerSocket(8888);
			client = server.accept();
			dis = new DataInputStream(client.getInputStream());
			System.out.println(dis.readUTF());
			//(5)获取输出流-->给客户端响应结果
			
			dos = new DataOutputStream(client.getOutputStream());
			dos.writeUTF("客户端您好:我是服务器,我收到了你的信息");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
		//(6)关闭流
			IOClose.closeAll(dos,dis,client,server);
		}
		
	}
}

Server2.java

package com.bjsxt.server;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class Server2 {
	public static void main(String[] args) {
		//(1)创建ServerSocket对象
		ServerSocket server=null;
		//(2)监听是否有客户端发送请求
		Socket client=null;
		BufferedReader br=null;
		
		try {
			server = new ServerSocket(8888);
			client = server.accept();
			//获取来自浏览器的请求信息
			br=new BufferedReader(new InputStreamReader(client.getInputStream(), "utf-8"));
			String str=null;
			while((str=br.readLine()).length()>0){
				System.out.println(str);
			}
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
		//(6)关闭流
			IOClose.closeAll(br,client,server);
		}
		
	}
}

Server3.java

package com.bjsxt.server;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Server3 {
	public static void main(String[] args) {
		//(1)创建ServerSocket对象
		ServerSocket server=null;
		//(2)监听是否有客户端发送请求
		Socket client=null;
		InputStream is=null;
		
		try {
			server = new ServerSocket(8888);
			client = server.accept();
			//获取来自浏览器的请求信息
			is=client.getInputStream();
			byte [] buf=new byte[20480];
			int len=is.read(buf);
			System.out.println(new String(buf,0,len));
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
		//(6)关闭流
			IOClose.closeAll(is,client,server);
		}
		
	}
}

IOClose.java

package com.bjsxt.server;

import java.io.Closeable;
import java.io.IOException;

public class IOClose {
	//关闭全部的工具类
	public static void closeAll(Closeable...c){
		for (Closeable closeable : c) {
			if (closeable!=null) {
				try {
					closeable.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}

index.html

<html>
	<head>
		<title>我的第一个html</title>
 	</head>
        <body>
		<h1>hello world</h1>
		<form action="http://localhost:8888/log" method="get">
			<p>用户名:<input  type="text" id="uname" name="username" /></p>
			<p>密码:<input  type="password" id="pwd" name="password"/></p>
			<p>兴趣爱好<input type="checkbox" name="hobby" value="ball"/><input type="checkbox" name="hobby" value="read"/>读书
			<input type="checkbox" name="hobby" value="paint"/>画画</p>
			<p><input type="submit" value="登录"/></p>

		</form>
	</body>

</html>

3.预备知识_HTTP协议入门

3.1协议

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

3.2HTTP协议简介

HTTP:Hypertext Transfer Protocol超文本传输协议,是网络应用层的协议,建立在TCP/IP协议基础上,HTTP使用可靠的TCP连接,默认端口为80.
用户打开web浏览器(常见的HTTP客户端),输入URL地址,就能接收到远程HTTP服务器端发送过来的网页,即HTTP遵循请求(Request)/应答(Response)模型。web浏览器向web服务器发送请求,web服务器处理请求并返回适当的应答,所有HTTP连接都被构造成一套请求与应答。
在这里插入图片描述
HTTP客户端和服务器分别由不同的软件开发商提供,它们都可以用任意的编程语言编写,如用.NET 编写的客户程序与用Java编写的服务器程序顺利通信,就必须遵守HTTP协议,这样才能彼此都懂对方发送的消息,HTTP协议严格规定了HTTP请求和HTTP响应的数据格式。

3.3HTTP请求格式

1)请求方式、URL(统一资源定位符)、HTTP协议/版本
2)请求头 Request Header
a)请求头包含许多有关客户端环境和请求正文的有用信息。例如,请求头可以声明浏览器所有的语言,请求正文的长度等。
3)请求正文 Requet Content (只有在 post 方式才有)
请求头和请求正文之间必须有符号行(回车符或行结束符),将请求头与请求正文分开,这个行非常重要,它表示 请求头已结束,接下来的是请求正文。通常post方式的数据存放于此,请求正文中可以包含客户提交的查询字符串等信息。在实际应用中,HTTP请求正文可以包含更多的内容。
在这里插入图片描述

3.4HTTP 响应格式

  1. HTTP 协议版本、状态代码、描述
  2. 响应头(Response Head)
  3. 响应正文(Respose Content)

httpServer_2

Server.java

package com.bjsxt.server;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
	public static void main(String[] args) {
		//(1)创建ServerSocket对象
		ServerSocket server=null;
		//(2)监听是否有客户端发送请求
		Socket client=null;
		//(3)获取Scoket对象
				//(4)获取输入流  -->得到客户端的请求
		DataInputStream dis=null;
		DataOutputStream dos=null;
		try {
			server = new ServerSocket(8888);
			client = server.accept();
			dis = new DataInputStream(client.getInputStream());
			System.out.println(dis.readUTF());
			//(5)获取输出流-->给客户端响应结果
			
			dos = new DataOutputStream(client.getOutputStream());
			dos.writeUTF("客户端您好:我是服务器,我收到了你的信息");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
		//(6)关闭流
			IOClose.closeAll(dos,dis,client,server);
		}
		
	}
}

Server2.java

package com.bjsxt.server;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class Server2 {
	public static void main(String[] args) {
		//(1)创建ServerSocket对象
		ServerSocket server=null;
		//(2)监听是否有客户端发送请求
		Socket client=null;
		BufferedReader br=null;
		
		try {
			server = new ServerSocket(8888);
			client = server.accept();
			//获取来自浏览器的请求信息
			br=new BufferedReader(new InputStreamReader(client.getInputStream(), "utf-8"));
			String str=null;
			while((str=br.readLine()).length()>0){
				System.out.println(str);
			}
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
		//(6)关闭流
			IOClose.closeAll(br,client,server);
		}
		
	}
}

Server3.java

package com.bjsxt.server;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class Server3 {
	public static void main(String[] args) {
		String CRLF="\r\n";//换行
		String BLANK=" ";//空格
		//(1)创建ServerSocket对象
		ServerSocket server=null;
		//(2)监听是否有客户端发送请求
		Socket client=null;
		InputStream is=null;
		
		try {
			server = new ServerSocket(8888);
			client = server.accept();
			//获取来自浏览器的请求信息
			is=client.getInputStream();
			byte [] buf=new byte[20480];
			int len=is.read(buf);
			System.out.println(new String(buf,0,len));
			/**对Web浏览器的请求作出响应*/
			StringBuilder sb=new StringBuilder();
			StringBuilder sbContent=new StringBuilder();//响应的文本
			sbContent.append("<html><head><title>响应结果</title></head>");
			sbContent.append("<body>登录成功</body></html>");
			
			//(1)拼接响应头
			sb.append("HTTP/1.1").append(BLANK).append(200).append(BLANK).append("OK");
			sb.append(CRLF);//换行
			sb.append("Content-Type: text/html;charset=utf-8");
			sb.append(CRLF);//换行
			sb.append("Content-Length:").append(sbContent.toString().getBytes().length).append(CRLF);
			sb.append(CRLF);//换行,代表响应头与响应的正文部门之间的空行
			sb.append(sbContent);
			
			//通过流输出 
			BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(client.getOutputStream(),"utf-8"));
			bw.write(sb.toString());
			bw.flush();
			bw.close();
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
		//(6)关闭流
			IOClose.closeAll(is,client,server);
		}
		
	}
}

IOClose.java

package com.bjsxt.server;

import java.io.Closeable;
import java.io.IOException;

public class IOClose {
	//关闭全部的工具类
	public static void closeAll(Closeable...c){
		for (Closeable closeable : c) {
			if (closeable!=null) {
				try {
					closeable.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}

4.Tomcat服务器的快速使用

4.1Tomcat简介

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

4.2Tomcat使用

1)配置Tomcat
a) JAVA_HOME Java JDK 的根目录
在这里插入图片描述
b) CATALINA_HOME Tomcat 根目录
在这里插入图片描述

bin:存放Tomcat脚本目录

  1. 启动和关闭 Tomcat
    启动 Tomcat 服务器:startup.bat
    关闭 Tomcat 服务器:shutdown.bat
    在这里插入图片描述
    conf:存放配置文件

在这里插入图片描述
conf—>server.xml:存放配置信息
存放配置端口信息
在这里插入图片描述
在这里插入图片描述
lib:存放Tomcat运行所需要的jar包
在这里插入图片描述
logs:存放日志文件

temp:存放Tomcat临时文件

webapps:部署项目到服务器
在 webapps 目录下新建目录存放.html 面页
访问页面

**work:**工作目录,将JSP文件的Java文件转换为class文件的工作目录。

部署项目到Tomcat
在webapps目录下创建自己的文件夹
在这里插入图片描述
在该文件夹下新建一个HTML文档

index.html

<html>
	<head>
		<title>发布到Tomcat中的项目的主页</title>
	</head>
	<body>
		<h1>hello tomcat</h1>
	</body>
</html>

bin目录下双击
在这里插入图片描述

启动Tomcat服务
在这里插入图片描述
在地址栏输入http://locathost:8080/myfirst/index.html

在这里插入图片描述

5.Tomcat服务器运行原理

5.1Tomcat的运行原理

客户浏览器发出要求访问特定德Servlet的请求。
1)Tomcat服务器接收到客户请求并解析
2)Tomcat服务器创建一个ServletRequest对象,在ServletRequest对象中包含了客户请求信息及其他关于客户的信息,如请求头,请求正文,以及客户机的IP地址等。
3)Tomcat服务器创建一个ServletResponse 对象
4)Tomcat服务器调用客户所请求的Servlet的service服务方法,并且把ServletRequest对象和ServletResponse对象做为参数传给该服务方法。
5) Servlet 从 ServletRequest 对象中可获取客户的请求信息。
6) Servlet 利用 ServletResponse 对象来生成响应结果。
7) Tomcat 服务器把 Servlet 生成的响应结果发送给客户。
8)
在这里插入图片描述

6.预备_http工具查看网络交互过程

6.1下载并安装

httpwatch安装包:https://pan.baidu.com/s/1ogcMmZSutx6WvIvh2VNpGg 密码:ggv4
在这里插入图片描述

6.2HttpWatch 的使用

  1. IE 浏览器查看浏览器栏HttpWatch Professional
    在这里插入图片描述
    最新版已经可以支持Google、Microsoft Edge等浏览器了下面是官方使用文档
    在这里插入图片描述

  2. 输入网址

  3. 分析数据

来源:https://blog.csdn.net/cldsj

HttpWatch学习笔记一
HttpWatch学习笔记二
HttpWatch学习笔记三

HttpWatch功能详细介绍
来源:https://www.cnblogs.com/Chilam007/p/6947235.html

7. 手写服务器_ 整体架构和接口_ 编写 XML配置文件

7.1 搭建项目框架

在这里插入图片描述

7.2 编写 XML 文档

<?xml version="1.0" encoding="UTF-8"?>
<web-app>
	<servlet>
		<servlet-name>login</servlet-name>
		<serlvet-class>com.bjsxt.servlet.LoginServlet</serlvet-class>
	</servlet>
	<servlet-mapping>
		<serlvet-name>login</serlvet-name>
		<url-pattern>/login</url-pattern>
	</servlet-mapping>
	<servlet-mapping>
		<serlvet-name>login</serlvet-name>
		<url-pattern>/log</url-pattern>
	</servlet-mapping>
</web-app>

7.3 编写 IOCloseUtil 类

package com.bjsxt.util;

import java.io.Closeable;
import java.io.IOException;

public class IOCloseUtil {//用于关系流
	public static void closeAll(Closeable...close){
		for (Closeable closeable : close) {
			if (closeable!=null) {
				try {
					closeable.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}

8.DOM4J 解析 XML 配置文件

8.1Entity 实体类的编写

package com.bjsxt.server;
/**
 * <servlet>
		<servlet-name>login</servlet-name>
		<serlvet-class>com.bjsxt.servlet.LoginServlet</serlvet-class>
	</servlet>
 * @author Administrator
 *
 */
public class Entity { /**servlet-name和一个servlet-name所对应的一个实体类*/
	private String name;//servlet-name
	private String clazz;//servlet-class
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getClazz() {
		return clazz;
	}
	public void setClazz(String clazz) {
		this.clazz = clazz;
	}
	public Entity(String name, String clazz) {
		super();
		this.name = name;
		this.clazz = clazz;
	}
	public Entity() {
		super();
	}
	
}

8.2Mapping 实体类的编写

package com.bjsxt.server;

import java.util.ArrayList;
import java.util.List;

/**
 * <servlet-mapping>
		<serlvet-name>login</serlvet-name>
		<url-pattern>/login</url-pattern>
		<url-pattern>/log</url-pattern>
	</servlet-mapping>
 * @author Administrator
 *
 */
public class Mapping {//映射关系,多个路径访问共享资源
	private String name;//servlet-name
	private List<String> urlPattern;//url-pattern
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public List<String> getUrlPattern() {
		return urlPattern;
	}
	public void setUrlPattern(List<String> urlPattern) {
		this.urlPattern = urlPattern;
	}
	public Mapping(){
		urlPattern=new ArrayList<String>();
	}
	public Mapping(String name, List<String> urlPattern) {
		super();
		this.name = name;
		this.urlPattern = urlPattern;
	}
	
}

8.3 解析 XML 文件

package com.bjsxt.server;

import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

public class WebDom4j {//用于解析XML
	private List<Entity> entityList;//用于存储是N多Entity,而每一个Entity都是servlet-name与servlet-class
	private List<Mapping> mappingList;//用于存储N多Mapping,而每一个Mapping都是一个servlet-name与N多个url-pattern
	public List<Entity> getEntityList() {
		return entityList;
	}
	public void setEntityList(List<Entity> entityList) {
		this.entityList = entityList;
	}
	public List<Mapping> getMappingList() {
		return mappingList;
	}
	public void setMappingList(List<Mapping> mappingList) {
		this.mappingList = mappingList;
	}
	//构造方法
	public WebDom4j() {
		entityList=new ArrayList<Entity>();
		mappingList=new ArrayList<Mapping>();
	}
	//获取Document对象的方法
	private Document getDocument(){
		try {
			//(1)创建SAXReader对象
			SAXReader reader=new SAXReader();
			//(2)调用read方法
			return reader.read(new File("src/WEB_INFO/web.xml"));
		} catch (DocumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	public void parse(Document doc){
		//(1)获取根元素
		Element root=doc.getRootElement(); //web-app
		//(2)获取servlet子元素
		for(Iterator<Element> ite=root.elementIterator("servlet");ite.hasNext();){
			Element subElement=ite.next();//得到每一个servlet
			//创建一个实体类
			Entity ent=new Entity(); //用于存储servlet-name与servlet-class
			for(Iterator<Element> subIte=subElement.elementIterator();subIte.hasNext();){
				Element ele=subIte.next(); //可能是servlet-name,也可能是servlet-class
				if("servlet-name".equals(ele.getName())){
					ent.setName(ele.getText()); //给实体类中的name赋值
				}else if("serlvet-class".equals(ele.getName())){
					ent.setClazz(ele.getText());
				}
			}
			//将Entity添加到集合中
			entityList.add(ent);
		}
		//测试
		/**for (Entity entity : entityList) {
			System.out.println(entity.getName()+"\t"+entity.getClazz());
		}*/
		//解析servlet-mapping
		
		for(Iterator<Element> ite=root.elementIterator("servlet-mapping");ite.hasNext();){
			Element subEle=ite.next();//得到每一个servlet-mapping
			//创建一个Mapping类的对象
			Mapping map=new Mapping();
			//解析servlet-mapping下的子元素
			for(Iterator<Element> subIte=subEle.elementIterator();subIte.hasNext();){
				Element ele=subIte.next();  //servlet-name,也有可能是url-pattern
				if("serlvet-name".equals(ele.getName())){
					map.setName(ele.getText());
				}else if("url-pattern".equals(ele.getName())){
					//获取集合对象,调用集合对象的添加方法,添加元素素
					map.getUrlPattern().add(ele.getText());
				}
			}
			//Mapping添加到集合中
			mappingList.add(map);
		}
		//测试
		for (Mapping m : mappingList) {
			System.out.println(m.getName());
			for(String s:m.getUrlPattern()){
				System.out.println(s);
			}
		}
	}
	//用于测试
	public static void main(String[] args) {
		WebDom4j web=new WebDom4j();
		web.parse(web.getDocument());
	}
}

9. 反射创建 Servlet 对象

9.1 编写 ServletContext 类

Servlet 上下文,就是一个容器,用于存储映射关系

package com.bjsxt.server;

import java.util.HashMap;
import java.util.Map;

/**
 * Servlet上下用,就是一个容器,
 * @author Administrator
 *
 */
public class ServletContext { //Entity与Mapping的映射关系
	private Map<String,String> servlet;//key是servlet-name  (Entity中的name),值serlvet-class Entity中的clazz
	private Map<String,String> mapping;//key是url-pattern (Mapping中的List集合中的每一个元素),value是serlvet-name,是Mapping中的name
	public Map<String, String> getServlet() {
		return servlet;
	}
	public void setServlet(Map<String, String> servlet) {
		this.servlet = servlet;
	}
	public Map<String, String> getMapping() {
		return mapping;
	}
	public void setMapping(Map<String, String> mapping) {
		this.mapping = mapping;
	}
	public ServletContext() {
		servlet=new HashMap<String,String>();
		mapping=new HashMap<String,String>();
	}
}

9.2 编写 WebApp 类

  1. 初始化程序运行的数据
  2. 根据不同的 url 创建所请求的 Servlet 对象
package com.bjsxt.server;

import java.util.List;
import java.util.Map;

import com.bjsxt.servlet.Servlet;

public class WebApp {//App的意思是应用程序
	private static ServletContext contxt;
	static{
		contxt=new ServletContext();
		//分别获取对应关系的Map集合
		Map<String,String> servlet=contxt.getServlet();
		Map<String,String> mapping=contxt.getMapping();
		//创建解析XML文件对象
		WebDom4j web=new WebDom4j();
		web.parse(web.getDocument());//解析xml
		//获取解析XML之后的List集合
		List<Entity> entityList=web.getEntityList();
		List<Mapping> mappingList=web.getMappingList();
		
		//将List集合中的数据存储到Map集合
		for (Entity entity : entityList) {
			servlet.put(entity.getName(), entity.getClazz());
		}
		//	System.out.println(servlet);
		for( Mapping map:mappingList){
			//遍历url-pattern的集合
			List<String> urlPattern=map.getUrlPattern();
			for(String s:urlPattern){
				mapping.put(s, map.getName());
			}
		}
		//System.out.println(mapping);
	}
	/**
	 * 根据url创建不同的Servlet对象
	 * @param url
	 * @return
	 */
	public static Servlet getServlet(String url){
		if (url==null||url.trim().equals("")) {
			return null;
		}
		//调用无参构造方法创建Servlet对象
		
		try {
			//如果url正确
			//根据url的key获取servlet-name的值 /log=login, /reg=register
			String servletName=contxt.getMapping().get(url);
			//根据servletName得到对应的servlet-class
			String servletClass=contxt.getServlet().get(servletName);  //得到的是一个完整个的包名+类的字符串
			//使用反射创建 Servlet对象
			Class<?> clazz=Class.forName(servletClass);
			Servlet servlet = (Servlet) clazz.newInstance();
			return servlet;
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	//测试
	public static void main(String[] args) {
		System.out.println(getServlet("/log"));
		System.out.println(getServlet("/login"));
	}
}

10. 封装 Request_method_url

10.1 编写 Server

  1. 启动服务
  2. 关闭服务
package com.bjsxt.server;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {//服务器,用于启动和停止服务
	private ServerSocket server;
	public static void main(String[] args) {
		Server server=new Server();//创建服务器对象
		server.start();
	}
	public void start(){
		this.start(8888);
	}
	public void start(int port){
		try {
			server=new ServerSocket(port);
			this.receive(); //调用接收请求信息的方法
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	private void receive() {
		try {
			//(1)监听
			Socket client=server.accept();
			
			//获取用户的请求
			/**InputStream is=client.getInputStream();
			byte []buf =new byte [20480];
			int len=is.read(buf);
			System.out.println(new String(buf,0,len));*/
			//封装请求信息
			Request req=new Request(client.getInputStream());
			//req.show();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	public void stop(){
		
	}
}

10.2 编写 HTML

login.html

<html>
	<head>
		<title>登录</title>
	</head>
	<body>
		<form action="http://localhost:8888/log" method="post">
			<p>用户名:<input  type="text" name="username" id="username"/></p>
			<p>密码:<input  type="password" name="pwd" id="password"/></p>
			<p>
				爱好:<input  type="checkbox" name="hobby" value="ball"/>足球
				      <input  type="checkbox" name="hobby" value="read"/>读书
					  <input type="checkbox" name="hobby" value="paint"/>画画
			</p>
			<p><input  type="submit" value="登录"/></p>
		</form>
	</body>
</html>

10.3 封装 Request_method_url

package com.bjsxt.server;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Request {/*请求*/
	private InputStream is;//输入流
	private String requestInfo;//请求字符串,请求方式,请求的路径,参数,协议,协议版本,请求的正文。。。
	private String method;//请求的方式
	private String url;//请求的url
	//输入框的name为key,值为value
	/*
	 *  key: username    value  :bjsxt
	 *  key:pwd          value:123
	 *  key:hobby        value   :read,ball
	 * */
	private Map<String,List<String>> parametermapValues;//参数
	private static final String CRLF="\r\n";//换行
	private static final String BLANK=" ";//空格
	//构造方法,初始化属性
	public Request() {
		parametermapValues=new HashMap<String,List<String>>();
		method="";
		url="";
		requestInfo="";
	}
	public Request(InputStream is){
		this();//调用本类无参的构造方法
		this.is=is;
		try {
			byte [] buf=new byte[20480];
			int len=this.is.read(buf);
			requestInfo=new String(buf,0,len);
		} catch (IOException e) {
			return;
		}
		//调用本类中的分解请求信息的方法
		this.parseRequestInfo();
	}
	//分解请求信息的方法
	/**
	 * 请求方式
	 * 请求路径
	 * 请求的参数
	 * 
	 */
	private void parseRequestInfo(){
		String paraString="";//用于存储请求参数
		//获取请求参数的第一行
		String firstLine=requestInfo.substring(0, requestInfo.indexOf(CRLF)).trim();//从0开始,到第一个换行的位置
		//分解出请求方式
		int index=firstLine.indexOf("/");
		this.method=firstLine.substring(0, index).trim();
		//分解url  ,get可能包含参数,也可能不包含参数post
		String urlString= firstLine.substring(index,firstLine.indexOf("HTTP/")).trim();
		//判断请求方式是GET还 是POST
		if("get".equalsIgnoreCase(this.method)){  //包含请求参数
			if (urlString.contains("?")) {
				String [] urlArray=urlString.split("\\?");
				this.url=urlArray[0];
				paraString=urlArray[1];
			}else{
				this.url=urlString;
			}
		}else{//post不包含请求参数
			this.url=urlString;
			paraString=requestInfo.substring(requestInfo.lastIndexOf(CRLF)).trim();
		}
		if (paraString.equals("")) {
			return;
		}
		//请求参数
		//System.out.println(paraString);
		
	}
	//用于测试
	/*public void show(){
		System.out.println(this.url);
		System.out.println(this.method);
	}*/
}

11. 封装 Request_ 存储参数_ 处理中文

11.1 编写分解参数的方法

package com.bjsxt.server;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Request {/*请求*/
	private InputStream is;//输入流
	private String requestInfo;//请求字符串,请求方式,请求的路径,参数,协议,协议版本,请求的正文。。。
	private String method;//请求的方式
	private String url;//请求的url
	//输入框的name为key,值为value
	/*
	 *  key: username    value  :bjsxt
	 *  key:pwd          value:123
	 *  key:hobby        value   :read,ball
	 * */
	private Map<String,List<String>> parametermapValues;//参数
	private static final String CRLF="\r\n";//换行
	private static final String BLANK=" ";//空格
	//构造方法,初始化属性
	public Request() {
		parametermapValues=new HashMap<String,List<String>>();
		method="";
		url="";
		requestInfo="";
	}
	public Request(InputStream is){
		this();//调用本类无参的构造方法
		this.is=is;
		try {
			byte [] buf=new byte[20480];
			int len=this.is.read(buf);
			requestInfo=new String(buf,0,len);
		} catch (IOException e) {
			return;
		}
		//调用本类中的分解请求信息的方法
		this.parseRequestInfo();
	}
	//分解请求信息的方法
	/**
	 * 请求方式
	 * 请求路径
	 * 请求的参数
	 * 
	 */
	private void parseRequestInfo(){
		String paraString="";//用于存储请求参数
		//获取请求参数的第一行
		String firstLine=requestInfo.substring(0, requestInfo.indexOf(CRLF)).trim();//从0开始,到第一个换行的位置
		//分解出请求方式
		int index=firstLine.indexOf("/");
		this.method=firstLine.substring(0, index).trim();
		//分解url  ,get可能包含参数,也可能不包含参数post
		String urlString= firstLine.substring(index,firstLine.indexOf("HTTP/")).trim();
		//判断请求方式是GET还 是POST
		if("get".equalsIgnoreCase(this.method)){  //包含请求参数
			if (urlString.contains("?")) {
				String [] urlArray=urlString.split("\\?");
				this.url=urlArray[0];
				paraString=urlArray[1];
			}else{
				this.url=urlString;
			}
		}else{//post不包含请求参数
			this.url=urlString;
			paraString=requestInfo.substring(requestInfo.lastIndexOf(CRLF)).trim();
		}
		if (paraString.equals("")) {
			return;
		}
		//请求参数
		//System.out.println(paraString);
		//调用本类中分解请求参数的方法
		this.parseParam(paraString);
		
	}
	//用于测试
	/*public void show(){
		System.out.println(this.url);
		System.out.println(this.method);
	}*/
	//username=bjsxt&pwd=123&hobby=ball&hobby=paint
	/**
	 * username=bjsxt
	 * pwd=123
	 * hobby=ball
	 * hobby=paint
	 * 
	 * username=
	 * @param prarString
	 */
	private void parseParam(String prarString){
		String [] token=prarString.split("&");
		for(int i=0;i<token.length;i++){
			String keyValues=token[i];
			String []keyValue=keyValues.split("=");  //username   bjsxt   pwd   123
			if (keyValue.length==1) {  //username=
				keyValue=Arrays.copyOf(keyValue, 2);
				keyValue[1]=null;
			}
			//将  表单元素的name与name对应的值存储到Map集合
			String key=keyValue[0].trim();
			String value=keyValue[1]==null?null:decode(keyValue[1].trim(), "utf-8");
			//放到集合中存储
			if (!parametermapValues.containsKey(key)) {
				parametermapValues.put(key, new ArrayList<String>());
			}
			List<String> values=parametermapValues.get(key);
			values.add(value);
		}
	}
	//根据表单元素的name获取多个值
	private String [] getParamterValues(String name){
		//根据key获取value
		List<String> values=parametermapValues.get(name);
		if (values==null) {
			return null;
		}else{
			return values.toArray(new String [0] );
		}
		
	}
	private String getParamter(String name){
		//调用本类中根据name获取多个值的方法
		String [] values=this.getParamterValues(name);
		if (values==null) {
			return null;
		}else{
			return values[0];
		}
	}
	
	//处理中文,因类浏览器对中文进行了编码,进行解码
	private String decode(String value,String code){
		try {
			return URLDecoder.decode(value, code);
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	public static void main(String[] args) {
		Request req=new Request();
		//调用分解参数的方法
		req.parseParam("username=%E5%8C%97%E4%BA%AC%E5%B0%9A%E5%AD%A6%E5%A0%82&pwd=123&hobby=ball&hobby=paint");
		System.out.println(req.parametermapValues);
		
		//调用获取多个值的方法
		String [] str=req.getParamterValues("hobby");
		for (String string : str) {
			System.out.println(string);
		}
		//调用获取单个值的方法
		System.out.println(req.getParamter("pwd"));
	}
	
}

11.2 编写根据页面上的 name 获取多个值的方法

11.3 编写根据页面上的 name 获取单个值的方法

11.4 编写解码的方法 1

1.5 测试

https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=0&rsv_idx=1&tn=baidu&wd=%E5%8C%97%E
4%BA%AC%E5%B0%9A%E5%AD%A6%E5%A0%82&rsv_pq=f65f15f40003a658&rsv_t=d74
7OilFeeFkf73QkinDYpKiB4y%2Ffj3mxWfE865IJ0VxoTk5Obwtc4z41pM&rqlang=cn&rsv_ente
r=1&rsv_sug3=19&rsv_sug1=21&rsv_sug7=101

12. 封装 Response

12.1 封装 Response

  1. 构造响应头
  2. 推送到客户端
package com.bjsxt.server;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;

import com.bjsxt.util.IOCloseUtil;

public class Response {//响应
	private StringBuilder headInfo;//响应头
	private StringBuilder content;//响应内容
	private int length;//响应内容的长度
	//流
	private BufferedWriter bw;
	
	//两个常量,换行和空格
	private static final String CRLF="\r\n";//换行
	private static final String BLANK=" ";//空格
	
	//构造方法
	public Response() {
		headInfo=new StringBuilder();
		content=new StringBuilder();
		
	}
	//带参构造方法
	public Response(OutputStream os){
		this();//调用本类的无参构造方法
		try {
			bw=new BufferedWriter(new OutputStreamWriter(os, "utf-8"));
		} catch (UnsupportedEncodingException e) {
			headInfo=null;
		}
		
	}
	//构造正文部分
	public Response print(String info){
		content.append(info);
		try {
			length+=info.getBytes("utf-8").length;
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return this;
	}
	public Response println(String info){
		content.append(info).append(CRLF);
		try {
			length+=(info+CRLF).getBytes("utf-8").length;
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return this;
	}
	
	//构造响应头
	
	private void createHeadInfo(int code){
		headInfo.append("HTTP/1.1").append(BLANK).append(code).append(BLANK);
		switch (code) {
		case 200:
			headInfo.append("OK");
			break;
		case 500:
			headInfo.append("SERVER ERROR");
			break;
		default:
			headInfo.append("NOT FOUND");
			break;
		}
		headInfo.append(CRLF);
		headInfo.append("Content-Type:text/html;charset=utf-8").append(CRLF);
		headInfo.append("Content-Length:"+length).append(CRLF);
		headInfo.append(CRLF);
	}
	/**
	 * 推送到客户机的浏览器
	 * @param code
	 */
	public void pushToClient(int code){
		if (headInfo==null) {
			code=500;
		}
		try {
			//调用本类中的构造响应头
			this.createHeadInfo(code);
			bw.write(headInfo.toString());
			bw.write(content.toString());
			bw.flush();
			this.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public void close(){
		IOCloseUtil.closeAll(bw);
	}
}

12.2 编写相应的 Servlet 构造响应内容

package com.bjsxt.servlet;

import com.bjsxt.server.Request;
import com.bjsxt.server.Response;

public abstract class Servlet { //是所有的请求的Servlet的父类
	public void service(Request req,Response rep) throws Exception{
		this.doGet( req, rep);
		this.doPost( req, rep);
	}
	public abstract void doGet(Request req,Response rep) throws Exception;
	public abstract void doPost(Request req,Response rep) throws Exception;
}

LogServlet.java

package com.bjsxt.servlet;

import com.bjsxt.server.Request;
import com.bjsxt.server.Response;

public class LoginServlet extends Servlet {

	@Override
	public void doGet(Request req, Response rep) throws Exception {
			//获取请求参数
		String name=req.getParameter("username");
		String pwd=req.getParameter("pwd");
		
		if(this.login(name, pwd)){
			//调用响应中的构建内容的方
			rep.println(name+"登录成功");
		}else{
			rep.println(name+"登录失败,对不起,账号或密码不正确");
		}
		
	}
	private boolean login(String name,String pwd){
		if ("bjsxt".equals(name)&&"123".equals(pwd)) {
			return true;
		}
		return false;
	}

	@Override
	public void doPost(Request req, Response rep) throws Exception {
		// TODO Auto-generated method stub
		
	}
}

12.3 启动服务器进行测试

13. 封装分发器_实现多线程

Dispatcher.java

package com.bjsxt.server;

import java.io.IOException;
import java.net.Socket;

import com.bjsxt.servlet.Servlet;
import com.bjsxt.util.IOCloseUtil;

/**
 * 一个请求与响应就是一个Dispatcher
 * @author Administrator
 *
 */
public class Dispatcher implements Runnable {
	private Request req;
	private Response rep;
	private Socket client;
	private int code=200;//状态码
	//构造方法初始化属性
	public Dispatcher(Socket client) {
		//将局部变量的值赋给成员变量
		this.client=client;
		try {
			req=new Request(this.client.getInputStream());
			rep=new Response(this.client.getOutputStream());
		} catch (IOException e) {
			code=500;
			return ;
		}
	}
	@Override
	public void run() {
		//根据不同的url创建指定的Servlet对象
		System.out.println(req.getUrl());
		Servlet servlet=WebApp.getServlet(req.getUrl());
		if (servlet==null) {
			this.code=404;
		}else{
			//调用相应的Servlet中的service方法
			try {
				servlet.service(req,rep);
			} catch (Exception e) {
				this.code=500;
			}
		}
		//将响应结果推送到客户机的浏览器
		rep.pushToClient(code);
		IOCloseUtil.closeAll(client);
	}

}

14. 整合最终版

14.1 查缺补漏完善项目

FaviconServlet.java

package com.bjsxt.servlet;

import com.bjsxt.server.Request;
import com.bjsxt.server.Response;

public class FaviconServlet extends Servlet {

	@Override
	public void doGet(Request req, Response rep) throws Exception {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void doPost(Request req, Response rep) throws Exception {
		// TODO Auto-generated method stub
		
	}

}

15. 总结和期望

15.1 项目总结

涉及知识点

  1. 集合
  2. IO
  3. 多线程
  4. 网络编程
  5. 面向对象
  6. 反射
  7. XML
  8. HTML

15.2 项目思路

  1. 先分析
  2. 搭建项目框架
  3. 一步一步实现,先从会的开始实现
  4. 整体思路,宏观使用面向对象,微观使用面向过程
  5. 注重代码的调试

15.3 思想是从键盘中敲出来的

  1. 多注重实战
  2. 学知识不是目的,目的是对知识的综合运用,才能将知识
    转化为能力
  3. 不要气馁,每天进步一点点,人最大的敌人是自己,今天
    的自己比昨天的自己有进步,就是成功。
  4. 今天所有的付出,都会在明天以 RMB 的形式还给你。
  5. 加油!!!

附源码:
链接:https://pan.baidu.com/s/1upUHY0bGXmLfLZKOt5ICxg
提取码:pfz1

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值