通过一个xml文件来确定服务器要如何响应相关请求
<?xml version="1.0" encoding="UTF-8"?><web-app><servlet><servlet-name>login</servlet-name><servlet-class>com/xys/server/basic/LoginServlet</servlet-class></servlet><servlet><servlet-name>reg</servlet-name><servlet-class>com/xys/server/basic/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>
在这个例子中如果浏览器做了/login或/g请求,则服务器就需要执行com.xys.server.basic.LoginServlet类中的操作
下面四个类用于解析web.xml以判断何时需要执行哪个类中的操作,再创建相应的响应类做出响应
package com.xys.server.basic;import java.util.HashMap;import java.util.List;import java.util.Map;//将entities容器和mappings容器转换为Map容器public class WebContext {private final Map<String, String> EntityMap = new HashMap<>();private final Map<String, String> MappingMap = new HashMap<>();public WebContext(List<Entity> entities, List<Mapping> mappings){for(Entity en :entities){EntityMap.put(en.getName(), en.getClz());}for(Mapping ma :mappings){for(String pa:ma.getPatterns() ){MappingMap.put(pa, ma.getName());}}}//通过url获取相应类的地址public String getClz(String pattern){return EntityMap.get(MappingMap.get(pattern));}}
package com.xys.server.basic;public class Entity {private String name;private String clz;//保存web.xml中一个servlet标签中的相关内容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;}}
package com.xys.server.basic;import java.util.HashSet;import java.util.Set;//保存web.xml中一个servlet-mapping标签中的相关内容public class Mapping {private String name;private Set<String> patterns;public Mapping(){patterns = new HashSet<String>();}public void setName(String name) {this.name = name;}public String getName() {return name;}public void setPatterns(Set<String> patterns) {this.patterns = patterns;}public Set<String> getPatterns() {return patterns;}public void addPatterns(String pattern){patterns.add(pattern);}}
package com.xys.server.basic;import org.xml.sax.Attributes;import org.xml.sax.helpers.DefaultHandler;import javax.xml.parsers.SAXParser;import javax.xml.parsers.SAXParserFactory;import java.net.Socket;import java.util.ArrayList;import java.util.List;//解析WebXMLpublic class SaxAnalyze {public void Analyze(String pattern, String parameter, Socket client) throws Exception {Response response = new Response(client);//1、获取解析工厂SAXParserFactory factory = SAXParserFactory.newInstance();//2、从解析工厂获取解析器SAXParser parser = factory.newSAXParser();//4、加载处理器WebHandle handle = new WebHandle();//5、解析:执行处理器中的方法,在该例子中,处理器会根据Web.xml中的内容创建servlet对象和servlet-mapping对象,并将其存入容器parser.parse(Thread.currentThread().getContextClassLoader().getResourceAsStream("com/xys/server/basic/Web.xml"), handle);//调用WebContext类将解析获取的servlet对象和servlet-mapping对象存入map,方便通过servlet-mapping找到对应的servlet,再通过servlet找到对应的类WebContext wc = new WebContext(handle.getEntities(),handle.getMappings());//通过url获取相应类的地址String index = "<html>" +"<head>" +"<title>" +"index" +"</title>" +"</head>" +"<body>" +"MyServer的回复:主页" +"</body>" +"</html>";//url请求为空则进入主页if(pattern == null || pattern.equals("") || pattern.equals("/"))response.PushToBrowser(200, index);else {//url请求不为空,则根据web.xml的内容创建相关类String name = wc.getClz(pattern);if (name != null) {name = name.replace("/", ".");//利用反射创建相关类Class<?> Clz = Class.forName(name);//声明一个响应接口,用该接口接收一个响应实现类对象,根据不同的请求会实例化不同的响应实现类对象Servlet servlet = (Servlet) Clz.getConstructor().newInstance();servlet.Service(parameter, client);//请求不存在返回404页} else {String errorContent = "<html>" +"<head>" +"<title>" +"error" +"</title>" +"</head>" +"<body>" +"MyServer的回复:404 NOT FOUND" +"</body>" +"</html>";response.PushToBrowser(404, errorContent);}}}//3、编写处理器private static class WebHandle extends DefaultHandler{private final List<Entity> entities = new ArrayList<>();private final List<Mapping> mappings = new ArrayList<>();private Entity entity;private Mapping mapping;String tag;boolean isMapping = false;//建servlet对象和servlet-mapping对象@Overridepublic void startElement(String uri, String localName, String qName, Attributes attributes) {tag = qName;if(tag.equals("servlet")){entity = new Entity();}else if(tag.equals("servlet-mapping")){mapping = new Mapping();isMapping = true;}}//根据XML中的内容为对象的属性赋值@Overridepublic void characters(char[] ch, int start, int length) {String str = new String(ch, start, length);if(tag != null && !str.equals("")){if(isMapping){if(tag.equals("servlet-name")){mapping.setName(str);}else if(tag.equals("url-pattern")){mapping.addPatterns(str);}}else{if(tag.equals("servlet-name")){entity.setName(str);}else if(tag.equals("servlet-class")){entity.setClz(str);}}}}//将对象放入容器@Overridepublic void endElement(String uri, String localName, String qName) {if (qName.equals("servlet")) {entities.add(entity);} else if (qName.equals("servlet-mapping")) {mappings.add(mapping);}tag = null;}public List<Entity> getEntities() {return entities;}public List<Mapping> getMappings() {return mappings;}}}
响应接口,所有响应类均实现该接口,这样在做出响应时只需要声明一个相应接口,然后调用接口中相应的方法即可,上面的方法会自行根据请求实例化不同的响应实现类
package com.xys.server.basic;import java.net.Socket;public interface Servlet {void Service(String parameter, Socket client);}
两个实现类:实现类可以接受请求参数,并执行将请求参数放入map中的处理操作
登录
package com.xys.server.basic;import java.net.Socket;import java.util.*;//登录public class LoginServlet implements Servlet{@Overridepublic void Service(String parameter, Socket client) {Map<String, List<String>> ServiceParameter = Convert.convertMap(parameter);System.out.println("参数为:"+parameter);for(String key:ServiceParameter.keySet()){System.out.print(key+": ");List<String> values = ServiceParameter.get(key);String[] valueArray = values.toArray(new String[0]);for(String value:valueArray){System.out.print(value+" ");}System.out.println();}Response response = new Response(client);//编写响应正文String content = "<html>" +"<head>" +"<title>" +"success" +"</title>" +"</head>" +"<body>" +"LoginServlet的回复:登录成功" +"</body>" +"</html>";response.PushToBrowser(200, content);}}
注册
package com.xys.server.basic;import java.io.IOException;import java.net.Socket;import java.util.List;import java.util.Map;//注册public class RegisterServlet implements Servlet{@Overridepublic void Service(String parameter, Socket client) {Map<String, List<String>> RegisterParameter = Convert.convertMap(parameter);System.out.println("参数为:"+parameter);for(String key:RegisterParameter.keySet()){System.out.print(key+": ");List<String> values = RegisterParameter.get(key);String[] valueArray = values.toArray(new String[0]);for(String value:valueArray){System.out.print(value+" ");}System.out.println();}try{Response response = new Response(client.getOutputStream());//编写响应正文String content = "<html>" +"<head>" +"<title>" +"success" +"</title>" +"</head>" +"<body>" +"RegisterServlet的回复:用户注册" +"</body>" +"</html>";response.PushToBrowser(200, content);} catch (IOException e) {e.printStackTrace();}}}
package com.xys.server.basic;import java.util.*;public class Convert {//将请求参数转为map容器public static Map<String, List<String>> convertMap(String parameter) {Map<String, List<String>> parameterM = new HashMap<>();String[] KeyValues = parameter.split("&");for (String query : KeyValues) {String[] kv = query.split("=");kv = Arrays.copyOf(kv, 2);//避免有些参数是空值导致kv的长度只有1String key = kv[0];String value = kv[1];//如果这个key还没有被存进map,就在容器中新建一个键值对if (!parameterM.containsKey(key))parameterM.put(key, new ArrayList<String>());//获取相应的key值所对应的value容器,将value存入parameterM.get(key).add(value);}return parameterM;}}
向浏览器发送响应的类,任何类都可以调用该类向浏览器发送响应,例如上面的两个响应实现类就有调用这个类
package com.xys.server.basic;import java.io.BufferedWriter;import java.io.IOException;import java.io.OutputStream;import java.io.OutputStreamWriter;import java.net.Socket;import java.nio.charset.StandardCharsets;import java.util.Date;//服务器响应请求public class Response {private BufferedWriter bw;private String content;private StringBuilder headInfo;private int len;private final String BLANK = " ";private final String CRLF = "\r\n";private Response(){content = "";headInfo = new StringBuilder();len = 0;}public Response(Socket client){this();try{bw = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));} catch (IOException e) {e.printStackTrace();}}public Response(OutputStream os){this();try{bw = new BufferedWriter(new OutputStreamWriter(os));} catch (Exception e) {e.printStackTrace();}}//建立响应协议并发送public void PushToBrowser(int code, String content) {if(headInfo == null)code = 505;len = content.getBytes(StandardCharsets.UTF_8).length;buildHead(code);try{bw.append(headInfo);bw.append(content);bw.flush();} catch (IOException e) {e.printStackTrace();}}//建立状态行和响应头private void buildHead(int code){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;}headInfo.append("Date:").append(new Date()).append(CRLF);headInfo.append("Server:MyServer/0.01;charset:UTF-8").append(CRLF);headInfo.append("Content-type:text/html").append(CRLF);headInfo.append("Content-length:").append(len).append(CRLF).append(CRLF);}}
主类,同时用于获取请求参数,并将参数传给响应实现类进行进一步操作
package com.xys.server.basic;import java.io.InputStream;import java.net.ServerSocket;import java.net.Socket;//主类public class Main {private static boolean isRunner = true;public static void main(String[] args) throws Exception {ServerSocket s_socket = new ServerSocket(8888);while(isRunner){Socket client = s_socket.accept();System.out.println("------------------------------");System.out.println("一个客户端建立了连接");new Thread(new Dispatcher(client)).start();}}//封装内部类:多线程获取不同的请求内容private static class Dispatcher implements Runnable{private final Socket client;public Dispatcher(Socket client){this.client = client;}@Overridepublic void run() {try{SaxAnalyze sa = new SaxAnalyze();//获取请求协议InputStream is = client.getInputStream();byte[] flush = new byte[1024 * 10];int len = is.read(flush);String info = new String(flush, 0, len);//从请求协议中分离URIint idx1 = info.indexOf("/");int idx2 = info.indexOf(" ", idx1);String pattern = info.substring(idx1, idx2);//从请求协议中分离参数String parameter = info.substring(info.lastIndexOf("\r\n") + 1);System.out.println("请求协议:" + "\r\n" + info);System.out.println("请求的服务为:" + pattern);//解析xml,并根据获得的URI创建对应的对象,并将获得的参数传给创建的对象sa.Analyze(pattern, parameter, client);client.close();} catch (Exception e) {e.printStackTrace();isRunner = false;}}}}
