实战:手写WebServer:(Web服务器)
项目上传到网盘(包含Firefox 浏览器 RESTer 插件):
链接:https://pan.baidu.com/s/1pzBuTMEQ55e-fKGSirhwdA
提取码:fry3
目的:承上启下,总结所学过得java基础知识+为即将到来的javaweb做准备
简介:
上网浏览网页,离不开服务器,客户请求页面,服务器响应内容,响应得内容是根据每个Web请求来产生动态内容得,其内部即启动多个线程来产生不同内容。这种请求响应式得交互,都是基于HTTP协议的。
涉及知识
- OOP 面对对象思想
- 容器
- IO流
- 多线程
- 网络编程
- 反射机制
- XML解析
- HTML
- HTTP协议
1.反射:
Reflection,把java类中的各种结构(方法、属性、构造器、类名)映射成一个个java对象。注意是在运行期,利用反射技术可以对一个类进行解剖,同时反射也是框架设计的灵魂!
package edu.hue.jk;
import java.lang.reflect.InvocationTargetException;
public class ReflectTest {
public static void main(String[] args) throws Exception{
//获取Class对象的三种方式
//1.对象.getClass()
Phone phone = new Phone();
Class clazz = phone.getClass();
//2.类.class();
clazz = Phone.class;
//3.Class.forName("包名.类名"); 这就是反射机制 传的是字符串 就很灵活了
clazz = Class.forName("edu.hue.jk.Phone");
//创建对象
Phone phone2 = (Phone)clazz.getConstructor().newInstance();
}
//手机类
static class Phone{
public Phone() {
}
}
}
2.XML解析
XML:Extensible Markup Language,可扩展标记语言,作为数据的一种存储格式或用于存储软件的参数,程序解析此配置文件,就可以达到不修改代码就能更改程序的目的。
package edu.hue.jk;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
public class XmlTest01 {
/**
* 熟悉SAX解析流程
* @author 超爱学习的可琛同学
* @throws IOException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static void main(String[] args) throws SAXException, IOException, ParserConfigurationException {
//SAX解析
//1、获取解析工厂
SAXParserFactory factory=SAXParserFactory.newInstance();
//2、从解析工厂获取解析器
SAXParser parse =factory.newSAXParser();
//3、编写处理器
//4、加载文档 Document 注册处理器
PersonHandler handler=new PersonHandler();
//5、解析
parse.parse(Thread.currentThread().getContextClassLoader()
.getResourceAsStream("edu/hue/jk/p.xml")
,handler);
List<Person> persons = handler.getPersons();
for (Person person : persons) {
System.out.println(person.getName()+"---"+person.getAge());
}
}
static class PersonHandler extends DefaultHandler{
private List<Person> persons;
private Person person;
private String tag;//存储当前操作的标签
//解析文档开始
@Override
public void startDocument() throws SAXException {
persons = persons = new ArrayList<Person>();
System.out.println("--文档解析开始--");
}
public java.util.List<Person> getPersons() {
return persons;
}
public void setPersons(java.util.List<Person> persons) {
this.persons = persons;
}
//解析标签元素开始
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
tag = qName;
if (qName.equals("person")) {
person = new Person();
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
String string = new String(ch,start,length);
if (tag != null && tag.equals("name")) {
person.setName(string);
}else if ( tag!=null && tag.equals("age")) {
person.setAge(Integer.valueOf(string));
}
}
//解析标签元素结束
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if(qName.equals("person")) {
persons.add(person);
}
tag = null;
}
//解析文档结束
@Override
public void endDocument() throws SAXException {
System.out.println("--文档解析结束--");
}
}
}
package edu.hue.jk;
//一个Javabean
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;
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Person() {
super();
}
}
<?xml version="1.0" encoding="UTF-8" ?>
<persons>
<person>
<name>至尊宝</name>
<age>9000</age>
</person>
<person>
<name>白晶晶</name>
<age>7000</age>
</person>
</persons>
接下来解析一个稍微复杂的XML
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
<servlet>
<servlet-name>login</servlet-name>
<servlet-class>edu.hue.jk.servlet.LoginServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>reg</servlet-name>
<servlet-class>edu.hue.jk.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>
package edu.hue.jk.servlet;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import edu.hue.jk.Person;
public class XmlTest02 {
public static void main(String[] args) throws Exception {
//SAX解析
//1、获取解析工厂
SAXParserFactory factory=SAXParserFactory.newInstance();
//2、从解析工厂获取解析器
SAXParser parse =factory.newSAXParser();
//3、编写处理器
//4、加载文档 Document 注册处理器
WebHandler handler=new WebHandler ();
//5、解析
parse.parse(Thread.currentThread().getContextClassLoader()
.getResourceAsStream("edu/hue/jk/servlet/web.xml")
,handler);
WebContext webContext = new WebContext(handler.getEntities(), handler.getMappings());
String classForName = webContext.getClz("/login");
Class clazz = Class.forName(classForName);
Servlet servlet = (Servlet)clazz.getConstructor().newInstance();
servlet.server();
}
static class WebHandler extends DefaultHandler{
private List<Entity> entities;
private List<Mapping> mappings;
private Entity entity;
private Mapping mapping;
public List<Mapping> getMappings() {
return mappings;
}
public List<Entity> getEntities() {
return entities;
}
private String tag;
private boolean isMapping = true;
//解析文档开始
@Override
public void startDocument() throws SAXException {
entities = new ArrayList<>();
mappings = new ArrayList<>();
System.out.println("--文档解析开始--");
}
//解析标签元素开始
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
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 {
String string = new String(ch,start,length);
if(!isMapping) {
if (tag != null && tag.equals("servlet-name")) {
entity.setName(string);
}else if ( tag!=null && tag.equals("servlet-class")) {
entity.setCls(string);
}
}else {
if (tag != null && tag.equals("servlet-name")) {
mapping.setName(string);
}else if ( tag!=null && tag.equals("url-pattern")) {
mapping.addPattern(string);
}
}
}
//解析标签元素结束
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if(qName.equals("servlet")) {
entities.add(entity);
}else if (qName.equals("servlet-mapping")) {
mappings.add(mapping);
}
tag = null;
}
//解析文档结束
@Override
public void endDocument() throws SAXException {
System.out.println("--文档解析结束--");
}
}
}
package edu.hue.jk.servlet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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 entity : entities) {
entityMap.put(entity.getName(), entity.getCls());
}
for(Mapping mapping : mappings) {
for(String pattern : mapping.getPatterns()) {
mappingMap.put(pattern, mapping.getName());
}
}
}
public String getClz(String pattern) {
return entityMap.get(mappingMap.get(pattern));
}
}
package edu.hue.jk.servlet;
public class Entity {
private String name;
private String cls;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCls() {
return cls;
}
public void setCls(String cls) {
this.cls = cls;
}
public Entity(String name, String cls) {
super();
this.name = name;
this.cls = cls;
}
public Entity() {
super();
}
}
package edu.hue.jk.servlet;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class Mapping {
private String name;
private Set<String> patterns ;
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 Mapping() {
patterns = new HashSet<>();
}
public void addPattern(String string) {
patterns.add(string);
}
}
package edu.hue.jk.servlet;
public interface Servlet {
void server();
}
package edu.hue.jk.servlet;
public class LoginServlet implements Servlet {
@Override
public void server() {
System.out.println("Login Server");
}
}
package edu.hue.jk.servlet;
public class RegisterServlet implements Servlet{
@Override
public void server() {
System.out.println("Register Server");
}
}
3.简单的HTML
<html>
<head>
<title>第一个html登录</title>
</head>
<body>
<h1>表单的使用</h1>
<pre>
post:提交 ,基于http协议不同 量大 请求参数url不可见 安全<br/>
get: 默认,获取,基于http协议不同 量小 请求参数url可见 不安全<br/>
action: 请求web服务器的资源 URL<br/>
name:作为后端使用,区分唯一: 请求服务器,必须存在,数据不能提交<br/>
id: 作为前端使用,区分唯一<br/>
</pre>
<form method="get" 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>
4.HTTP协议
HTTP:Hyper Text Transfer Protocol 超文本传输协议
- 请求协议
1.请求行:方法(GET/POST)URI 协议/版本
2.请求头:(Request Header)
3.请求正文
- 响应协议
1.状态行:协议\版本、状态码、状态描述
2.响应头:(Response Header)
3.响应正文
package edu.hue.jk.server;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 封装请求协议:获取method uri以及请求参数
* @author Mr . Xu's PC
*
*/
public class Request {
private List<String> list ;
private static final String CRLF = "\r\n";
//请求信息
private String requstInfo;
//请求方式
private String method;
//请求URL
private String url="";
//请求参数
private String queryStr;
//存储参数
private Map<String, List<String>> parameterMaP;
public Request(InputStream is) {
parameterMaP = new HashMap<>();
list = new ArrayList<>();
byte[] datas = new byte[1024*1024];
int len;
try {
len = is.read(datas);
requstInfo = new String(datas, 0, len);
System.out.println(requstInfo);
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
return;
}
parseRequestInfo();
}
public Request(Socket client) throws IOException {
this(client.getInputStream());
//构造器的相互调用
}
private void parseRequestInfo() {
System.out.println("--分解--");
method = requstInfo.substring(0, requstInfo.indexOf("/")).toLowerCase().trim();
url = requstInfo.substring(requstInfo.indexOf("/")+1, requstInfo.indexOf("HTTP/")).trim();
int queryIdx = url.indexOf("?");
if(queryIdx >= 0) {
String[] urls = url.split("\\?");
url = urls[0];
queryStr = urls[1];
}
if(method.equals("post")) {
String qStr = requstInfo.substring(requstInfo.lastIndexOf(CRLF)).trim();
queryStr = qStr;
}
System.out.println("method = "+method);
System.out.println("url = "+url );
System.out.println("queryStr = "+queryStr );
covertMap();
}
//将请求参数转Map
private void covertMap() {
String[] keyValue = this.queryStr.split("&");
for(String query : keyValue) {
String[] kv = query.split("=");
kv = Arrays.copyOf(kv, 2);
String key = kv[0].trim();
System.out.println(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) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
return null;
}
/**
* 通过name 获取对应的多个值
* @param key
* @return
*/
public List<String> getParameterValues(String key) {
List<String> list = this.parameterMaP.get(key);
if(null==list || list.size()<1) {
return null;
}
return list;
}
public String getMethod() {
return method;
}
public String getUrl() {
return url;
}
public String getQueryStr() {
return queryStr;
}
}
package edu.hue.jk.server;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Date;
import javax.lang.model.element.Element;
public class Response {
private Socket client;
private BufferedWriter bw;
private static final String BLANK = " ";
private static final String CRLF = "\r\n";
//正文
private StringBuilder content = new StringBuilder();
//协议头信息
private StringBuilder headInfo;
private int len ;//正文的字节数
public Response(Socket client) {
this.client = client;
content = new StringBuilder();
headInfo = new StringBuilder();
len = 0;
try {
bw = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
public Response(OutputStream os) {
bw = new BufferedWriter(new OutputStreamWriter(os));
}
//动态添加内容
public Response print(String info) {
content.append(info);
len += info.toString().getBytes().length;
return this;
}
public Response println(String info) {
content.append(info).append(CRLF);
len += (info+CRLF).toString().getBytes().length;
return this;
}
//推送响应信息
public void pushToBrowser(int code) {
createHeadInfo(code);
try {
bw.append(headInfo);
bw.append(content);
bw.flush();
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
//构建头信息
private void createHeadInfo(int code) {
headInfo.append("HTTP/1.1").append(BLANK).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;
}
//2.响应头
headInfo.append("Date:").append(new Date()).append(CRLF);
headInfo.append("Server:").append("zkc 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);
}
}
package edu.hue.jk.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;
import java.util.Date;
import javax.swing.text.html.HTMLDocument.HTMLReader.IsindexAction;
public class Server {
private static final String BLANK = " ";
private static final String CRLF = "\r\n";
private ServerSocket serverSocket ;
private static Socket client;
private static boolean isRunning = true;
public static void main(String[] args) throws Exception {
Server server01 = new Server();
server01.start();
while (isRunning) {
server01.receiver();
new Thread(new Dispachar(client)).start();;
}
}
//启动服务
public void start() {
try {
serverSocket = new ServerSocket(8888);
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
System.out.println("启动失败");
}
}
//接收连接
public void receiver() {
try {
client = serverSocket.accept();
System.out.println("一个client 建立了连接");
isRunning = true;
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
//停止服务
public void stop() {
isRunning = false;
}
}
源码已存网盘!
ps:其中还有404页面error未处理!