十二、手写WebServer(高琪java300集+java从入门到精通笔记)

客户请求页面,服务器响应内容,响应的内容是根据每个web请求来产生动态内容的,其内部即启动多个线程来产生不同内容。这种请求响应式的交互,都是基于HTTP协议的。

数据库服务器:存储数据

缓存服务器:提高性能

Web服务器:客户要什么东西响应什么东西

  1. OOP

  2. 容器

  3. IO

  4. 多线程

  5. 网络编程

  6. XML(Extensible Markup
    Language,可扩展标记语言)解析:作为数据的一种存储格式或用于存储软件(内存大小)的参数,程序解析此配置文件,就可以到达不修改代码就能更改程序的目的。

SAX(simple API for XML,流解析) org.xml.sax

public class XmlTest01 {

public static void main(String[] args) throws
ParserConfigurationException, SAXException, IOException {

//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(“com/sxt/server/basic/p.xml”)

,handler);//当前线程类加载器里获取 .换成/

//获取数据

List persons = handler.getPersons();

for(Person p:persons) {

System.out.println(p.getName()+"–>"+p.getAge());

}

}

}

class PersonHandler extends DefaultHandler{

private List persons ;

private Person person ;

private String tag; //存储操作标签

@Override

public void startDocument() throws SAXException {

persons = new ArrayList();

System.out.println("----解析文档开始----");

}

@Override

public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {

if(null!=qName) {

tag = qName; //存储标签名

if(tag.equals(“person”)) {

person = new Person();

}

}

System.out.println(qName+"–>解析开始");//标签名/元素名//println

}

@Override

public void characters(char[] ch, int start, int length)
throws SAXException {

String contents = new String(ch,start,length).trim();//去括号
if(null!=tag) { //处理了空

if(tag.equals(“name”)) {

person.setName(contents);

}else if(tag.equals(“age”)) {

if(contents.length()>0) {

person.setAge(Integer.valueOf(contents));//String->Integer

}

}

}

if(contents.length()>0) {

System.out.println(“内容为->”+contents);

}else {

System.out.println(“内容为->”+“空”);

}

}

@Override

public void endElement(String uri, String localName, String qName)
throws SAXException {

if(null!=qName) { //为tag时错

至尊宝

9000//end后tag置为空

白晶晶

7000

if(qName.equals(“person”)) {

persons.add(person);

}

}

tag = null; //tag丢弃了

}

System.out.println(qName+"–>解析结束开始");

}

@Override

public void endDocument() throws SAXException {

System.out.println("----解析文档结束----");

}

public List getPersons() {

return persons;

}

}

Webxml:

login

com.sxt.server.basic.servlet.LoginServlet

reg

com.sxt.server.basic.servlet.RegisterServlet

public class Entity {

private String name;

private String clz;

public Entity() {//无参构造器//有参构造方法

// TODO Auto-generated constructor stub

}

//set/get方法

}

login

/login

/g

reg

/reg

public class Mapping {

private String name;

private Set patterns ;

public Mapping() {

patterns = new HashSet();//无参构造器new
Mapping时new容器//有参//set/get

}

public void addPattern(String pattern) {

this.patterns.add(pattern);

}}

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(“com/sxt/server/basic/servlet/web.xml”)

,handler);

//获取数据

WebContext context = new
WebContext(handler.getEntitys(),handler.getMappings());

public class WebContext {

private List entitys =null;

private List mappings =null;

//key–>servlet-name value -->servlet-class

private Map entityMap =new HashMap();

//key -->url-pattern value -->servlet-name

private Map mappingMap =new HashMap();

public WebContext(List entitys, List mappings) {

this.entitys = entitys;

this.mappings = mappings;

//将entity 的List转成了对应map

for(Entity entity:entitys) {

entityMap.put(entity.getName(), entity.getClz());

}

//将map 的List转成了对应map

for(Mapping mapping:mappings) {

for(String pattern: mapping.getPatterns()) {

mappingMap.put(pattern, mapping.getName());

}

}

}

/**

* 通过URL的路径找到了对应class

* @param pattern

* @return

*/

public String getClz(String pattern) {

String name = mappingMap.get(pattern);//map里的get方法

return entityMap.get(name);

}

}

//假设你输入了 /login

String className = context.getClz("/reg");

Class clz =Class.forName(className);

Servlet servlet =(Servlet)clz.getConstructor().newInstance();//动态创建对象
接口!!!!

System.out.println(servlet);

servlet.service();

}

}

class WebHandler extends DefaultHandler{

private List entitys = new ArrayList();

private List mappings = new ArrayList();

private Entity entity ;

private Mapping mapping ;

private String tag; //存储操作标签

private boolean isMapping = false;

@Override

public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {

if(null!=qName) {

tag = qName; //存储标签名

if(tag.equals(“servlet”)) {

entity = new Entity();

isMapping = false;

}else if(tag.equals(“servlet-mapping”)) {

mapping = new Mapping();

isMapping = true;

}

}

}

@Override

public void characters(char[] ch, int start, int length)
throws SAXException {

String contents = new String(ch,start,length).trim();

if(null!=tag) { //处理了空

if(isMapping) { //操作servlet-mapping

if(tag.equals(“servlet-name”)) {

mapping.setName(contents);

}else if(tag.equals(“url-pattern”)) {

mapping.addPattern(contents);

}

}else { //操作servlet

if(tag.equals(“servlet-name”)) {

entity.setName(contents);

}else if(tag.equals(“servlet-class”)) {

entity.setClz(contents);

}

}

}

}

@Override

public void endElement(String uri, String localName, String qName)
throws SAXException {

if(null!=qName) {

if(qName.equals(“servlet”)) {

entitys.add(entity);

}else if(qName.equals(“servlet-mapping”)) {

mappings.add(mapping);

}

}

tag = null; //tag丢弃了

}

public List getEntitys() {

return entitys;

}

public List getMappings() {

return mappings;

}

}

  1. 反射

  2. 原来new一个对象需要JVM在字节码存储区域/类加载器中丢一个字节码信息(模子),模子虚拟机准备的可以用来new对象,现在由使用者准备这个模子

  3. 镜子:结构信息

反射Reflection:把java类中的各种结构(方法、属性、构造器、类名)映射成一个个Java对象(运行期制作)。利用反射技术对一个类进行解剖,反射是框架(经常变动)设计的灵魂。

3)对象是表示或封装一些数据。
一个类被加载后,JVM会创建一个对应该类的Class对象,类的整个结构信息会放到对应的Class对象中。

这个Class对象就像一面镜子一样,通过这面镜子我可以看到对应类的全部信息。

一个类只对应一个Class对象 所以hashcode值一样

十二、手写WebServer(高琪java300集+java从入门到精通笔记)_第1张图片

十二、手写WebServer(高琪java300集+java从入门到精通笔记)_第2张图片

//三种方式

//1、对象.getClass()//iphone

Iphone iphone =new Iphone();

Class clz = iphone.getClass();

//2、类.class()//图纸

clz = Iphone.class;

//3、Class.forName(“包名.类名”)//地址去偷

clz = Class.forName(“com.sxt.server.basic.Iphone”);

//创建对象

/*Iphone iphone2 =(Iphone)clz.newInstance();

System.out.println(iphone2);*/JDK9一下

Iphone iphone2 =(Iphone)clz.getConstructor().newInstance();

System.out.println(iphone2);
十二、手写WebServer(高琪java300集+java从入门到精通笔记)_第3张图片

  1. html(HyperText Markup
    Language,超文本标记语言):简单理解为浏览器使用的语言,所有WWW文件都必须遵守这个标准。

  2. 和http和url互联网三的基石,发展为html5和Xhtml(发展为XML)

-开始标签

网页上的控制信息页面标题</p> <p>

页面显示的内容

-结束标签

第一个html登录

表单的使用

post:提交 ,基于http协议不同 量大 请求参数url不可见 安全

get: 默认,获取,基于http协议不同 量小 请求参数url可见 不安全

action: 请求web服务器的资源 URL

name:作为后端使用,区分唯一: 请求服务器,必须存在,数据不能提交

id: 作为前端使用,区分唯一

用户名:

密码:

  1. http(HyperText Transfer
    Protocol,超文本传输协议):TCP/IP传输层协议,http应用层协议。

请求协议:

请求行:方法(GET/POST)、URi(Uniform Resource
Identifier,统一资源标识符,相对)、协议/版本

请求头:(Request Header)

请求正文:

  1. GET:

请求行

GET /index.html?name=test&pwd=123456 HTTP/1.1

请求体

XXXXXXXXX

请求正文

  1. POST:

请求行

POST /index.html HTTP/1.1

请求体

XXXXXXXXX

请求正文

name=test&pwd=123456

响应协议:

1.状态行:协议/版本 状态码 状态描述

HTTP/1.0 200 OK(2xx成功——表示请求已经被成功接受、理解、接受。如:200 OK
客户端请求成功 4xx客户端错误——请求有语法错误或请求无法实现。如404,Not Found
请求资源不存在,例如,输入了错误的URL)

2.请求头:

XXXXX

Server:

Content-tyoe:text/html text/plain

Content-length:3972546

  1. 请求正文

XXXXXXX

手写服务器:

获取请求协议:

  1. 创建ServerSocket

  2. 建立连接获取Socket

  3. 通过输入流获取请求协议

注意:GET/POST不一致的方法 火狐上RESTer

返回响应协议:

  1. 准备内容

  2. 获取字节数的长度

  3. 拼接响应协议

注意:空格与换行

  1. 使用输出流输出

public class Server02 {

private ServerSocket serverSocket ;

public static void main(String[] args) {

Server02 server = new Server02();

server.start();

}

//启动服务

public void start() {

try {

serverSocket = new ServerSocket(8888);//服务器端口

receive();

} catch (IOException e) {

e.printStackTrace();

System.out.println(“服务器启动失败…”);

}

}

//接受连接处理

public void receive() {

try {

Socket client = serverSocket.accept();

System.out.println(“一个客户端建立了连接…”);

//获取请求协议

InputStream is =client.getInputStream();

byte[] datas = new byte[1024*1024];

int len = is.read(datas);

String requestInfo = new String(datas,0,len);

System.out.println(requestInfo);

StringBuilder content =new StringBuilder();

content.append("");

content.append("");

content.append("");</p> <p>content.append(“服务器响应成功”);</p> <p>content.append("");

content.append("");

content.append("");

content.append(“shsxt server终于回来了。。。。”);

content.append("");

content.append("");

int size = content.toString().getBytes().length; //必须获取字节长度

StringBuilder responseInfo =new StringBuilder();

String blank =" ";

String CRLF =
“\r\n”;//\r是回车,\n是换行,前者使光标到行首,后者使光标下移一格。

//返回

//1、响应行: HTTP/1.1 200 OK

responseInfo.append(“HTTP/1.1”).append(blank);

responseInfo.append(200).append(blank);

responseInfo.append(“OK”).append(CRLF);

//2、响应头(最后一行存在空行):

/*

Date:Mon,31Dec209904:25:57GMT

Server:shsxt Server/0.0.1;charset=GBK

Content-type:text/html

Content-length:39725426

*/

responseInfo.append(“Date:”).append(new Date()).append(CRLF);

responseInfo.append(“Server:”).append(“shsxt Server/0.0.1;charset=GBK”).append(CRLF);

responseInfo.append(“Content-type:text/html”).append(CRLF);

responseInfo.append(“Content-length:”).append(size).append(CRLF);

responseInfo.append(CRLF);

//3、正文

responseInfo.append(content.toString());

//写出到客户端

BufferedWriter bw =new BufferedWriter(new
OutputStreamWriter(client.getOutputStream()));

bw.write(responseInfo.toString());

bw.flush();

} catch (IOException e) {

e.printStackTrace();

System.out.println(“客户端错误”);

}

}

//停止服务

public void stop() {

}

}

封装响应信息:

  1. 动态添加内容print

  2. 累加字节数的长度(内容)

  3. 根据状态码拼接响应头协议

  4. 根据状态码统一推送出去

调用处:动态调用print+传入状态码推送

调用: Response response =new Response(client);

//关注了内容

response.print("");

response.print("");

response.print("");</p> <p>response.print(“服务器响应成功”);</p> <p>response.print("");

response.print("");

response.print("");

response.print(“shsxt server终于回来了。。。。”);

response.print("");

response.print("");//先构建了内容再推状态码(包括响应信息长度)

//关注了状态码

response.pushToBrowser(200);

public class Response {

private BufferedWriter bw;

//正文

private StringBuilder content;

//协议头(状态行与请求头 回车)信息

private StringBuilder headInfo;

private int len; //正文的字节数

private final String BLANK =" ";

private final String CRLF = “\r\n”;

private Response() {//空构造无用

content =new StringBuilder();

headInfo=new StringBuilder();

len =0;

}

public Response(Socket client) {

this();

try {

bw=new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));

} catch (IOException e) {

e.printStackTrace();

headInfo = null;

}

}

public Response(OutputStream os) {

this();

bw=new BufferedWriter(new OutputStreamWriter(os));

}

//动态添加内容

public Response print(String info) {

content.append(info);

len+=info.getBytes().length;

return this;

}

public Response println(String info) {

content.append(info).append(CRLF);

len+=(info+CRLF).getBytes().length;

return this;

}

//推送响应信息

public void pushToBrowser(int code) throws IOException {

if(null ==headInfo) {

code = 505;

}

createHeadInfo(code);

bw.append(headInfo);

bw.append(content);

bw.flush();

}

//构建头信息

private void createHeadInfo(int code) {

//1、响应行: HTTP/1.1 200 OK

headInfo.append(“HTTP/1.1”).append(BLANK);

headInfo.append(code).append(BLANK);

switch(code) {

case 200:

headInfo.append(“OK”).append(CRLF);

break;

case 404:

headInfo.append(“NOT FOUND”).append(CRLF);

break;

case 505:

headInfo.append(“SERVER ERROR”).append(CRLF);

break;

}

//2、响应头(最后一行存在空行):

headInfo.append(“Date:”).append(new Date()).append(CRLF);

headInfo.append(“Server:”).append(“shsxt 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);

}

}

封装请求信息:通过分解字符串获取method URL和请求参数
POST请求参数可能在请求体中还存在。

通过Map封装请求参数 两个方法 考虑一个参数多个值和中文

public class Request1 {

//协议信息

private String requestInfo;

//请求方式

private String method;

//请求url

private String url;

//请求参数

private String queryStr;

//存储参数

private Map> parameterMap;

private final String CRLF = “\r\n”;

public Request1(Socket client) throws IOException {

this(client.getInputStream());//this

}

public Request1(InputStream is) {

byte[] datas = new byte[1024*1024];

int len;

try {

len = is.read(datas);

this.requestInfo = new String(datas,0,len);

} catch (IOException e) {

e.printStackTrace();

return ;

}

//分解字符串

parseRequestInfo();

}

private void parseRequestInfo() {

System.out.println("------分解-------");

System.out.println("—1、获取请求方式: 开头到第一个/------");

this.method = this.requestInfo.substring(0,
this.requestInfo.indexOf("/")).toLowerCase();//post/get

this.method=this.method.trim();//去空格 POST出现问题

System.out.println("—2、获取请求url: 第一个/ 到 HTTP/------");

System.out.println("—可能包含请求参数? 前面的为url------");

//1)、获取/的位置

int startIdx = this.requestInfo.indexOf("/")+1;//左闭右开

//2)、获取 HTTP/的位置

int endIdx = this.requestInfo.indexOf(“HTTP/”);

//3)、分割字符串

this.url = this.requestInfo.substring(startIdx, endIdx);

//4)、获取?的位置

int queryIdx =this.url.indexOf("?");

if(queryIdx>=0) {//表示存在请求参数

String[] urlArray = this.url.split("\\?");//正则

this.url =urlArray[0];

queryStr =urlArray[1];

}

System.out.println(this.url);

System.out.println("—3、获取请求参数:如果Get已经获取,如果是post可能在请求体中------");

if(method.equals(“post”)) {

String qStr
=this.requestInfo.substring(this.requestInfo.lastIndexOf(CRLF)).trim();//从CRLF至结尾
去空格

System.out.println(qStr+"–>");

if(null==queryStr) {

queryStr =qStr;

}else {

queryStr +="&"+qStr;//name=aa&pwd=123

}

}

queryStr = null==queryStr?"":queryStr;

System.out.println(method+"–>"+url+"–>"+queryStr);

//转成Map fav=1*&fav*=2*&uname*=shsxt&age=18&others=

convertMap();

}

//处理请求参数为Map

private void convertMap() {

//1、分割字符串 &

String[] keyValues =this.queryStr.split("&");

for(String queryStr:keyValues) {

//2、再次分割字符串 =

String[] kv = queryStr.split("=");

kv =Arrays.copyOf(kv, 2);

//获取key和value

String key = kv[0];

String value = kv[1]==null?null:decode( kv[1],“utf-8”);

//存储到map中

if(!parameterMap.containsKey(key)) { //第一次

parameterMap.put(key, new ArrayList());//map加

}

parameterMap.get(key).add(value);//list加

}

}

/**

* 处理中文

* @return

*/

private String decode(String value,String enc) {

try {

return java.net.URLDecoder.decode(value, enc);

} catch (UnsupportedEncodingException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

return null;

}

/**

* 通过name获取对应的多个值

* @param key

* @return

*/

public String[] getParameterValues(String key) {

List values = this.parameterMap.get(key);

if(null==values || values.size()<1) {

return null;

}

return values.toArray(new String[0]);//toArray(new Object[0]) 和
toArray() 在功能上是相同的

}

/**

* 通过name获取对应的一个值

* @param key

* @return

*/

public String getParameter(String key) {

String [] values =getParameterValues(key);//调用上面的

return values ==null?null:values[0];

}

public String getMethod() {

return method;

}

public String getUrl() {

return url;

}

public String getQueryStr() {

return queryStr;

}

}

Servlet(Server
Applet,小服务程序)将业务代码解耦到对应的业务类中(具体的Servlet)

整合配置文件

根据配置文件动态的读取类名,再进行反射获取具体的Servlet来处理业务,真正的以不变应万变

Dispatcher

加入了多线程,可一同时处理多个请求/使用的是短连接

InputStream is
=Thread.currentThread().getContextClassLoader().getResourceAsStream(“XXX.html”);

response.print((new String(is.readAllBytes())));

response.pushToBrowser(200);

首页


@param key

* @return

*/

public String[] getParameterValues(String key) {

List values = this.parameterMap.get(key);

if(null==values || values.size()<1) {

return null;

}

return values.toArray(new String[0]);//toArray(new Object[0]) 和
toArray() 在功能上是相同的

}

/**

* 通过name获取对应的一个值

* @param key

* @return

*/

public String getParameter(String key) {

String [] values =getParameterValues(key);//调用上面的

return values ==null?null:values[0];

}

public String getMethod() {

return method;

}

public String getUrl() {

return url;

}

public String getQueryStr() {

return queryStr;

}

}

Servlet(Server
Applet,小服务程序)将业务代码解耦到对应的业务类中(具体的Servlet)

整合配置文件

根据配置文件动态的读取类名,再进行反射获取具体的Servlet来处理业务,真正的以不变应万变

Dispatcher

加入了多线程,可一同时处理多个请求/使用的是短连接

InputStream is
=Thread.currentThread().getContextClassLoader().getResourceAsStream(“XXX.html”);

response.print((new String(is.readAllBytes())));

response.pushToBrowser(200);

首页

你可能感兴趣的:(javaSE笔记)