客户请求页面,服务器响应内容,响应的内容是根据每个web请求来产生动态内容的,其内部即启动多个线程来产生不同内容。这种请求响应式的交互,都是基于HTTP协议的。
数据库服务器:存储数据
缓存服务器:提高性能
Web服务器:客户要什么东西响应什么东西
OOP
容器
IO
多线程
网络编程
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
for(Person p:persons) {
System.out.println(p.getName()+"–>"+p.getAge());
}
}
}
class PersonHandler extends DefaultHandler{
private List
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时错
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
return persons;
}
}
Webxml:
public class Entity {
private String name;
private String clz;
public Entity() {//无参构造器//有参构造方法
// TODO Auto-generated constructor stub
}
//set/get方法
}
public class Mapping {
private String name;
private Set
public Mapping() {
patterns = new HashSet
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
private List
//key–>servlet-name value -->servlet-class
private Map
//key -->url-pattern value -->servlet-name
private Map
public WebContext(List
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
private List
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
return entitys;
}
public List
return mappings;
}
}
反射
原来new一个对象需要JVM在字节码存储区域/类加载器中丢一个字节码信息(模子),模子虚拟机准备的可以用来new对象,现在由使用者准备这个模子
镜子:结构信息
反射Reflection:把java类中的各种结构(方法、属性、构造器、类名)映射成一个个Java对象(运行期制作)。利用反射技术对一个类进行解剖,反射是框架(经常变动)设计的灵魂。
3)对象是表示或封装一些数据。
一个类被加载后,JVM会创建一个对应该类的Class对象,类的整个结构信息会放到对应的Class对象中。
这个Class对象就像一面镜子一样,通过这面镜子我可以看到对应类的全部信息。
一个类只对应一个Class对象 所以hashcode值一样
//三种方式
//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();
html(HyperText Markup
Language,超文本标记语言):简单理解为浏览器使用的语言,所有WWW文件都必须遵守这个标准。
和http和url互联网三的基石,发展为html5和Xhtml(发展为XML)
-开始标签
网页上的控制信息-结束标签
post:提交 ,基于http协议不同 量大 请求参数url不可见 安全
get: 默认,获取,基于http协议不同 量小 请求参数url可见 不安全
action: 请求web服务器的资源 URL
name:作为后端使用,区分唯一: 请求服务器,必须存在,数据不能提交
id: 作为前端使用,区分唯一
请求协议:
请求行:方法(GET/POST)、URi(Uniform Resource
Identifier,统一资源标识符,相对)、协议/版本
请求头:(Request Header)
请求正文:
请求行
GET /index.html?name=test&pwd=123456 HTTP/1.1
请求体
XXXXXXXXX
请求正文
请求行
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
XXXXXXX
手写服务器:
获取请求协议:
创建ServerSocket
建立连接获取Socket
通过输入流获取请求协议
注意:GET/POST不一致的方法 火狐上RESTer
返回响应协议:
准备内容
获取字节数的长度
拼接响应协议
注意:空格与换行
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("
content.append(“服务器响应成功”);
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() {
}
}
封装响应信息:
动态添加内容print
累加字节数的长度(内容)
根据状态码拼接响应头协议
根据状态码统一推送出去
调用处:动态调用print+传入状态码推送
调用: Response response =new Response(client);
//关注了内容
response.print("");
response.print("
");response.print("
response.print(“服务器响应成功”);
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
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
}
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
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
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);