补充知识点
反射:把java类中的各种结构方法(方法、属性、构造器、类名)映射成一个个的java对象
1、获取Class对象
三种方式:
对象…getClass()
Iphone iphone = new Iphone(); Class cls = iphone.getClass();
类.class()
clz = iphone.getClass();
推荐:Class.forName(“包名.类名”)
Class.forName(“完整路径”);
2、可以动态创建对象(用构造器创建)
Iphone iphone2 = (Iphone)clz.getConstructor().newInstance();
HTTP协议
请求协议:
每个请求由三部分组成
ep:GET /index.html HTTP/1.0(CRLF) //请求行以CRLF结束 CR:回车符,asc编码中对应数字13 LF:换行符,asc编码中对应数字10
响应协议:
ep:HTTP/1.0 、200、OK(CRLF)
具体实现
获取请求协议
1、创建ServerSocket
2、建立连接获取Socket
3、通过输入流获取请求协议(Get与Post不一致的地方)
package com.lijing.server.core;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
private ServerSocket serverSocket;
private boolean isRunning;
public static void main(String[] args){
Server server = new Server();
server.start();
}
//启动服务
public void start(){
try {
serverSocket = new ServerSocket(8888);
isRunning = true;
receive();
} catch (IOException e) {
e.printStackTrace();
System.out.println("服务器启动失败");
stop();
}
}
//接受连接处理
public void receive(){
while (isRunning) {
try {
Socket client = serverSocket.accept();
System.out.println("一个客户端建立了连接");
//多线程处理
new Thread(new Dispatcher(client)).start();
} catch (IOException e) {
e.printStackTrace();
System.out.println("客户端错误");
}
}
}
//停止服务
public void stop(){
isRunning =false;
try {
this.serverSocket.close();
System.out.println("服务器已停止");
} catch (IOException e) {
e.printStackTrace();
}
}
}
返回响应协议
1、准备内容
2、获取字节数的长度
3、拼接响应协议 (注意空格与换行)
4、使用输出流输出
封装响应协议 Response
1、动态添加内容print
2、累加字节数的长度
3、根据状态吗拼接响应头协议
4、根据状态码统一推送出去
调用处:动态调用print+传入状态码推送
package com.lijing.server.core;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Date;
/*
Response
1、动态添加内容print
2、累加字节数的长度
3、根据状态码拼接响应头协议
4、根据状态码同一推送出去
*/
public class Response {
BufferedWriter bw;
//正文
private StringBuilder content;
//协议头信息(状态行与请求头 回车)信息
private StringBuilder headInfo;
private int len ;//正文的字节数
private final String BLANK = " ";
private final String CRLF="\r\n";
public 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;
}
creatHeadInfo(code);//先有内容才构建头信息
bw.append(headInfo);
bw.append(content);
bw.flush();
}
//构建头信息
private void creatHeadInfo(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 FOUNF").append(CRLF);
break;
case 505:
headInfo.append("SERVER ERROR").append(CRLF);
break;
}
//2、响应头(最后一行存在空行) 字节数
headInfo.append("Date:").append(new Date()).append(CRLF);
headInfo.append("Server06:").append("server:Server02/0.0.1;charset=GBK").append(CRLF);
headInfo.append("Content-type:test/html").append(CRLF);
headInfo.append("Content-length:").append(len).append(CRLF);
headInfo.append(CRLF);
}
}
封装请求信息Request
1、通过分解字符串获取method,URL和请求参数(POST请求参数可能在请求体中还存在)
2、通过Map封装请求参数 两个方法(考虑一个参数多个值convertMap(),和中文decode)
package com.lijing.server.core;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.util.*;
/*
封装请求协议:封装请求参数为Map
*/
public class Request {
private final String CRLF = "\r\n";
//协议信息
private String requestInfo;
//请求方式
private String method;
//请求url
private String url;
//请求参数
private String queryStr;
//存储参数
private Map> parameterMap;
public Request(Socket client) throws IOException {
this(client.getInputStream());
}
public Request(InputStream is) {
parameterMap = new HashMap>();
byte[] datas = new byte[1024 * 1024];
int len = 0;
try {
len = is.read(datas);
this.requestInfo = new String(datas, 0, len);
} catch (IOException e) {
e.printStackTrace();
return;
}
//分解字符串
this.parseRequestInfo();
}
//分解字符串
private void parseRequestInfo() {
System.out.println("----分解开始----");
//System.out.println(requestInfo);
System.out.println("-----1、获取请求方式:开头到第一个/-----");
this.method = this.requestInfo.substring(0, this.requestInfo.indexOf("/")).toLowerCase().trim();
System.out.println(method);
System.out.println("-----1、获取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).trim();
System.out.println(this.url);
//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();
if (null == queryStr) {
queryStr = qStr;
} else {
queryStr += "&" + qStr;
}
}
queryStr = null == queryStr ? "" : queryStr;
System.out.println(method + "->" + url + "->" + queryStr);
//转成Map fav=1&fav=2&uname=lijing&others=
convertMap();
}
//处理请求参数为Map
public void convertMap() {
//1、分割字符串
String[] KeyValues = this.queryStr.split("&");
for (String quertstr : KeyValues) {
//再次分割字符串=
// System.out.println("jdjdksa--->"+quertstr);
String[] kv = quertstr.split("=");
kv = Arrays.copyOf(kv, 2);//保证有两个值 参数others=null时赋空
//获取key和value
String key = kv[0];
String value = kv[1]==null?null:decode(kv[1],"utf-8");
//存储到map中 现在map容器中找是否有相同key值若有加入其对应value,若无加入容器中
//System.out.println("hhhhhh"+kv[0]+"--->"+kv[1]);
if (!parameterMap.containsKey(key)) {//第一次
parameterMap.put(key, new ArrayList());
}
parameterMap.get(key).add(value);
}
}
//处理中文
private String decode(String value,String enc){
try {
return java.net.URLDecoder.decode(value,enc);//enc为传入处理的字符集
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
/*
通过name获取对应的多个值
*/
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]);//转换为数组
}
/*
通过name获取对应的一个值
*/
public String getParameter(String key) {
String[] values = getParameterValues(key);
return values == null?null:values[0];
}
public String getUrl() {
return url;
}
public String getMethod() {
return method;
}
public String getQueryStr() {
return queryStr;
}
}
引入Servlet
1、将业务代码解耦到对应的业务类中(具体的Servlet,这也是web阶段主要写的内容。ep:登陆业务
先写好loginServlet 再在配置文件web.xml中进行相应配置
即配置"url-pattern" /login 对外公布接口
写前台页面login.html时在action中加入路径
)
package com.lijing.server.core;
/*
服务器小脚本接口
*/
public interface Servlet {
void service(Request request,Response response);
}
业务类 LoginServlet
package com.lijing.user;
import com.lijing.server.core.Request;
import com.lijing.server.core.Response;
import com.lijing.server.core.Servlet;
public class LoginServlet implements Servlet{
public void service(Request request,Response response){
response.print("");
response.print("");
response.print("");
response.print("<第一个小脚本>");
response.print(" ");
response.print("");
response.print("");
response.print("<...欢迎回来:"+request.getParameter("uname"));
response.print("");
response.print("");
}
}
整合配置文件"web.xml"
1、根据配置文件动态的读取类名,再进行反射获取具体的Servlet来处理业务,真正的以不变应万变
login
com.lijing.user.LoginServlet
login
/login
/g
/hhh
reg
com.lijing.user.RegisterServlet
reg
/reg
/r
others
com.lijing.user.OthersServlet
others
/o
封装分发器Dispatcher
1、加入了多线程,可以同时处理多个请求,使用的时短连接
404及首页处理
读取错误、首页内容即可
package com.lijing.server.core;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
/*
分发器:加入状态内容处理 404,505,首页
*/
public class Dispatcher implements Runnable{
private Socket client;
private Request request;
private Response response;
public Dispatcher(Socket client){
this.client =client;
try {
//获取请求协议
//获取响应协议
request =new Request(client);
response = new Response(client);
} catch (IOException e) {
e.printStackTrace();
this.release();
}
}
@Override
public void run() {
try {
// System.out.println("hhhhh"+request.getUrl().equals("")+"kj"+request.getUrl());
Servlet servlet = WebApp.getServletFromUrl(request.getUrl());
System.out.println("servlet:"+servlet);
if(null != servlet) {
servlet.service(request, response);
//关注了状态码
response.pushToBrowser(200);
}else{
//错误页面
response.print("页面找不到了");
response.pushToBrowser(404);
}
}catch (Exception e){
try {
response.println("Dispatcher出错了^*^");
response.pushToBrowser(500);
}
catch (Exception e1){
e1.printStackTrace();
}
}
release();//短连接,用完释放
}
//释放资源
private void release(){
try {
client.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
存储xml元素的类Entity
package com.lijing.server.core;
/*格式
login
com.lijing.LoginServlet
*/
public class Entity {
private String name;
private String clz;
public Entity(){
}
public void setName(String name) {
this.name = name;
}
public void setClz(String clz) {
this.clz = clz;
}
public String getName() {
return name;
}
public String getClz() {
return clz;
}
}
存储.xml文件元素的类Mapping
package com.lijing.server.core;
import java.util.HashSet;
import java.util.Set;
/*
login
/login
/g
*/
public class Mapping {
private String name;
private Set patterns =new HashSet();
public Mapping() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set getPatterns() {
return patterns;
}
public void setPatterns(Set patterns) {
this.patterns = patterns;
}
public void addPattern(String pattern){
this.patterns.add(pattern);
}
}
解析配置文件.xml
package com.lijing.server.core;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
public class WebApp {
private static WebContext webContext;
static {
try {
//SAX解析
//1、获取解析工厂
// System.out.println("asdkjekk1");
SAXParserFactory factory =SAXParserFactory.newInstance();
//2、从解析工厂获取SAX解析器
SAXParser parser = factory.newSAXParser();
//3、创建Handler子类并实例化(编写处理器,加载文档Document注册处理器)
//System.out.println("asdkjekk3");
WebHandler handler = new WebHandler();
//System.out.println("asdkjekk4");
//4、重写该类中必要的方法
//5、解析
parser.parse(Thread.currentThread().getContextClassLoader()
.getResourceAsStream("com/lijing/server/core/web.xml"),
handler);
//6、获取数据
// System.out.println("asdkjekk5");
webContext = new WebContext(handler.getEntitys(),handler.getMappings());
//System.out.println("asdkje"+webContext);
}catch (Exception e){
System.out.println("Webapp解析配置文件错误");
}
}
/*
通过url获取配置对应的servlet
*/
public static Servlet getServletFromUrl(String url){
//假设你输入了/login
String className = webContext.getClz("/"+url);
//System.out.println(className+"aaaaa"+className);
Class clz=null;
try {
clz = Class.forName(className);
//反射实例化
Servlet servlet = (Servlet)clz.getConstructor().newInstance();
return servlet;
} catch (Exception e) {
e.printStackTrace();
}
return null;
// System.out.println(servlet);
//servlet.service();
}
}
处理器 WebHandler
package com.lijing.server.core;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
import java.util.ArrayList;
import java.util.List;
/*
处理器
*/
public 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;
public void startElement(String uri, String localName, String qName, Attributes attributes) {
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;
}
}
}
public void characters(char[] ch,int start,int length){
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);
}
}
}
}
public void endElement(String uri,String localName, String qName){
if(null!=qName){
if(qName.equals("servlet")){
entitys.add(entity);
}else if(qName.equals("servlet-mapping")){
mappings.add(mapping);
}
}
tag=null;
}
public List getEntitys() {
return entitys;
}
public List getMappings() {
return mappings;
}
}
WebContext 相当于工具类,把数据传过来处理
package com.lijing.server.core;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class WebContext {
private List entitys = null;
private List mappings = null;
//key-->servlet-name value-->servlet-class
private Map entityMap = new HashMap();
//key-->servlet-pattern value-->servlet-name
private Map mappingMap = new HashMap();
public WebContext(List entitys, List mappings) {
this.entitys = entitys;
this.mappings = mappings;
//将mapping的List转成对应的Map
for(Mapping mapping:mappings){
// System.out.println(mapping.getName()+"nnnn");//问题出在这里!!!!!!!!!mapping正常
Set tmappings = mapping.getPatterns();//按理tmappings应该不为0!!!!!!不知道为什么?????
// System.out.println(tmappings.size()); //////////////////////////////////////////////
for(String pattern:tmappings){ //!!!!!!找出来了,问题在webHandler37行tag等于pattern时没加入
mappingMap.put(pattern,mapping.getName());
//System.out.println("mapmap:::"+pattern+mapping.getName());
}
}
//将entity的List转成对应的Map
for(Entity entity:entitys){
entityMap.put(entity.getName(),entity.getClz());
// System.out.println("entity:::"+entity.getName());
}
}
//通过url的路径找到对应的class
public String getClz(String pattern){
//System.out.println(pattern+"DSAJDASKJDAWDJW");
//System.out.println(entitys.size()+"cccc"+mappings.size());
//System.out.println(entityMap.size()+"dddd"+mappingMap.size());
String name = mappingMap.get(pattern);
//System.out.println("webname"+name+"HJGJHG"+entityMap.get(name));
return entityMap.get(name);
}
}
login.html文件
用户登录
用户登录
package com.lijing.server.core中是服务器核心的部分,我们只关注package com.lijing.user;中的部分。根据不同的处理写不同的页面,写不同的后台逻辑