通过一个xml文件来确定服务器要如何响应相关请求

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <web-app>
    3. <servlet>
    4. <servlet-name>login</servlet-name>
    5. <servlet-class>com/xys/server/basic/LoginServlet</servlet-class>
    6. </servlet>
    7. <servlet>
    8. <servlet-name>reg</servlet-name>
    9. <servlet-class>com/xys/server/basic/RegisterServlet</servlet-class>
    10. </servlet>
    11. <servlet-mapping>
    12. <servlet-name>login</servlet-name>
    13. <url-pattern>/login</url-pattern>
    14. <url-pattern>/g</url-pattern>
    15. </servlet-mapping>
    16. <servlet-mapping>
    17. <servlet-name>reg</servlet-name>
    18. <url-pattern>/reg</url-pattern>
    19. </servlet-mapping>
    20. </web-app>

    在这个例子中如果浏览器做了/login或/g请求,则服务器就需要执行com.xys.server.basic.LoginServlet类中的操作
    下面四个类用于解析web.xml以判断何时需要执行哪个类中的操作,再创建相应的响应类做出响应

    1. package com.xys.server.basic;
    2. import java.util.HashMap;
    3. import java.util.List;
    4. import java.util.Map;
    5. //将entities容器和mappings容器转换为Map容器
    6. public class WebContext {
    7. private final Map<String, String> EntityMap = new HashMap<>();
    8. private final Map<String, String> MappingMap = new HashMap<>();
    9. public WebContext(List<Entity> entities, List<Mapping> mappings){
    10. for(Entity en :entities){
    11. EntityMap.put(en.getName(), en.getClz());
    12. }
    13. for(Mapping ma :mappings){
    14. for(String pa:ma.getPatterns() ){
    15. MappingMap.put(pa, ma.getName());
    16. }
    17. }
    18. }
    19. //通过url获取相应类的地址
    20. public String getClz(String pattern){
    21. return EntityMap.get(MappingMap.get(pattern));
    22. }
    23. }
    1. package com.xys.server.basic;
    2. public class Entity {
    3. private String name;
    4. private String clz;
    5. //保存web.xml中一个servlet标签中的相关内容
    6. public Entity(){
    7. }
    8. public void setName(String name) {
    9. this.name = name;
    10. }
    11. public void setClz(String clz) {
    12. this.clz = clz;
    13. }
    14. public String getName() {
    15. return name;
    16. }
    17. public String getClz() {
    18. return clz;
    19. }
    20. }
    1. package com.xys.server.basic;
    2. import java.util.HashSet;
    3. import java.util.Set;
    4. //保存web.xml中一个servlet-mapping标签中的相关内容
    5. public class Mapping {
    6. private String name;
    7. private Set<String> patterns;
    8. public Mapping(){
    9. patterns = new HashSet<String>();
    10. }
    11. public void setName(String name) {
    12. this.name = name;
    13. }
    14. public String getName() {
    15. return name;
    16. }
    17. public void setPatterns(Set<String> patterns) {
    18. this.patterns = patterns;
    19. }
    20. public Set<String> getPatterns() {
    21. return patterns;
    22. }
    23. public void addPatterns(String pattern){
    24. patterns.add(pattern);
    25. }
    26. }
    1. package com.xys.server.basic;
    2. import org.xml.sax.Attributes;
    3. import org.xml.sax.helpers.DefaultHandler;
    4. import javax.xml.parsers.SAXParser;
    5. import javax.xml.parsers.SAXParserFactory;
    6. import java.net.Socket;
    7. import java.util.ArrayList;
    8. import java.util.List;
    9. //解析WebXML
    10. public class SaxAnalyze {
    11. public void Analyze(String pattern, String parameter, Socket client) throws Exception {
    12. Response response = new Response(client);
    13. //1、获取解析工厂
    14. SAXParserFactory factory = SAXParserFactory.newInstance();
    15. //2、从解析工厂获取解析器
    16. SAXParser parser = factory.newSAXParser();
    17. //4、加载处理器
    18. WebHandle handle = new WebHandle();
    19. //5、解析:执行处理器中的方法,在该例子中,处理器会根据Web.xml中的内容创建servlet对象和servlet-mapping对象,并将其存入容器
    20. parser.parse(Thread.currentThread().getContextClassLoader().getResourceAsStream("com/xys/server/basic/Web.xml"), handle);
    21. //调用WebContext类将解析获取的servlet对象和servlet-mapping对象存入map,方便通过servlet-mapping找到对应的servlet,再通过servlet找到对应的类
    22. WebContext wc = new WebContext(handle.getEntities(),handle.getMappings());
    23. //通过url获取相应类的地址
    24. String index = "<html>" +
    25. "<head>" +
    26. "<title>" +
    27. "index" +
    28. "</title>" +
    29. "</head>" +
    30. "<body>" +
    31. "MyServer的回复:主页" +
    32. "</body>" +
    33. "</html>";
    34. //url请求为空则进入主页
    35. if(pattern == null || pattern.equals("") || pattern.equals("/"))
    36. response.PushToBrowser(200, index);
    37. else {
    38. //url请求不为空,则根据web.xml的内容创建相关类
    39. String name = wc.getClz(pattern);
    40. if (name != null) {
    41. name = name.replace("/", ".");
    42. //利用反射创建相关类
    43. Class<?> Clz = Class.forName(name);
    44. //声明一个响应接口,用该接口接收一个响应实现类对象,根据不同的请求会实例化不同的响应实现类对象
    45. Servlet servlet = (Servlet) Clz.getConstructor().newInstance();
    46. servlet.Service(parameter, client);
    47. //请求不存在返回404页
    48. } else {
    49. String errorContent = "<html>" +
    50. "<head>" +
    51. "<title>" +
    52. "error" +
    53. "</title>" +
    54. "</head>" +
    55. "<body>" +
    56. "MyServer的回复:404 NOT FOUND" +
    57. "</body>" +
    58. "</html>";
    59. response.PushToBrowser(404, errorContent);
    60. }
    61. }
    62. }
    63. //3、编写处理器
    64. private static class WebHandle extends DefaultHandler{
    65. private final List<Entity> entities = new ArrayList<>();
    66. private final List<Mapping> mappings = new ArrayList<>();
    67. private Entity entity;
    68. private Mapping mapping;
    69. String tag;
    70. boolean isMapping = false;
    71. //建servlet对象和servlet-mapping对象
    72. @Override
    73. public void startElement(String uri, String localName, String qName, Attributes attributes) {
    74. tag = qName;
    75. if(tag.equals("servlet")){
    76. entity = new Entity();
    77. }else if(tag.equals("servlet-mapping")){
    78. mapping = new Mapping();
    79. isMapping = true;
    80. }
    81. }
    82. //根据XML中的内容为对象的属性赋值
    83. @Override
    84. public void characters(char[] ch, int start, int length) {
    85. String str = new String(ch, start, length);
    86. if(tag != null && !str.equals("")){
    87. if(isMapping){
    88. if(tag.equals("servlet-name")){
    89. mapping.setName(str);
    90. }else if(tag.equals("url-pattern")){
    91. mapping.addPatterns(str);
    92. }
    93. }else{
    94. if(tag.equals("servlet-name")){
    95. entity.setName(str);
    96. }else if(tag.equals("servlet-class")){
    97. entity.setClz(str);
    98. }
    99. }
    100. }
    101. }
    102. //将对象放入容器
    103. @Override
    104. public void endElement(String uri, String localName, String qName) {
    105. if (qName.equals("servlet")) {
    106. entities.add(entity);
    107. } else if (qName.equals("servlet-mapping")) {
    108. mappings.add(mapping);
    109. }
    110. tag = null;
    111. }
    112. public List<Entity> getEntities() {
    113. return entities;
    114. }
    115. public List<Mapping> getMappings() {
    116. return mappings;
    117. }
    118. }
    119. }

    响应接口,所有响应类均实现该接口,这样在做出响应时只需要声明一个相应接口,然后调用接口中相应的方法即可,上面的方法会自行根据请求实例化不同的响应实现类

    1. package com.xys.server.basic;
    2. import java.net.Socket;
    3. public interface Servlet {
    4. void Service(String parameter, Socket client);
    5. }

    两个实现类:实现类可以接受请求参数,并执行将请求参数放入map中的处理操作
    登录

    1. package com.xys.server.basic;
    2. import java.net.Socket;
    3. import java.util.*;
    4. //登录
    5. public class LoginServlet implements Servlet{
    6. @Override
    7. public void Service(String parameter, Socket client) {
    8. Map<String, List<String>> ServiceParameter = Convert.convertMap(parameter);
    9. System.out.println("参数为:"+parameter);
    10. for(String key:ServiceParameter.keySet()){
    11. System.out.print(key+": ");
    12. List<String> values = ServiceParameter.get(key);
    13. String[] valueArray = values.toArray(new String[0]);
    14. for(String value:valueArray){
    15. System.out.print(value+" ");
    16. }
    17. System.out.println();
    18. }
    19. Response response = new Response(client);
    20. //编写响应正文
    21. String content = "<html>" +
    22. "<head>" +
    23. "<title>" +
    24. "success" +
    25. "</title>" +
    26. "</head>" +
    27. "<body>" +
    28. "LoginServlet的回复:登录成功" +
    29. "</body>" +
    30. "</html>";
    31. response.PushToBrowser(200, content);
    32. }
    33. }

    注册

    1. package com.xys.server.basic;
    2. import java.io.IOException;
    3. import java.net.Socket;
    4. import java.util.List;
    5. import java.util.Map;
    6. //注册
    7. public class RegisterServlet implements Servlet{
    8. @Override
    9. public void Service(String parameter, Socket client) {
    10. Map<String, List<String>> RegisterParameter = Convert.convertMap(parameter);
    11. System.out.println("参数为:"+parameter);
    12. for(String key:RegisterParameter.keySet()){
    13. System.out.print(key+": ");
    14. List<String> values = RegisterParameter.get(key);
    15. String[] valueArray = values.toArray(new String[0]);
    16. for(String value:valueArray){
    17. System.out.print(value+" ");
    18. }
    19. System.out.println();
    20. }
    21. try{
    22. Response response = new Response(client.getOutputStream());
    23. //编写响应正文
    24. String content = "<html>" +
    25. "<head>" +
    26. "<title>" +
    27. "success" +
    28. "</title>" +
    29. "</head>" +
    30. "<body>" +
    31. "RegisterServlet的回复:用户注册" +
    32. "</body>" +
    33. "</html>";
    34. response.PushToBrowser(200, content);
    35. } catch (IOException e) {
    36. e.printStackTrace();
    37. }
    38. }
    39. }
    1. package com.xys.server.basic;
    2. import java.util.*;
    3. public class Convert {
    4. //将请求参数转为map容器
    5. public static Map<String, List<String>> convertMap(String parameter) {
    6. Map<String, List<String>> parameterM = new HashMap<>();
    7. String[] KeyValues = parameter.split("&");
    8. for (String query : KeyValues) {
    9. String[] kv = query.split("=");
    10. kv = Arrays.copyOf(kv, 2);//避免有些参数是空值导致kv的长度只有1
    11. String key = kv[0];
    12. String value = kv[1];
    13. //如果这个key还没有被存进map,就在容器中新建一个键值对
    14. if (!parameterM.containsKey(key))
    15. parameterM.put(key, new ArrayList<String>());
    16. //获取相应的key值所对应的value容器,将value存入
    17. parameterM.get(key).add(value);
    18. }
    19. return parameterM;
    20. }
    21. }

    向浏览器发送响应的类,任何类都可以调用该类向浏览器发送响应,例如上面的两个响应实现类就有调用这个类

    1. package com.xys.server.basic;
    2. import java.io.BufferedWriter;
    3. import java.io.IOException;
    4. import java.io.OutputStream;
    5. import java.io.OutputStreamWriter;
    6. import java.net.Socket;
    7. import java.nio.charset.StandardCharsets;
    8. import java.util.Date;
    9. //服务器响应请求
    10. public class Response {
    11. private BufferedWriter bw;
    12. private String content;
    13. private StringBuilder headInfo;
    14. private int len;
    15. private final String BLANK = " ";
    16. private final String CRLF = "\r\n";
    17. private Response(){
    18. content = "";
    19. headInfo = new StringBuilder();
    20. len = 0;
    21. }
    22. public Response(Socket client){
    23. this();
    24. try{
    25. bw = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
    26. } catch (IOException e) {
    27. e.printStackTrace();
    28. }
    29. }
    30. public Response(OutputStream os){
    31. this();
    32. try{
    33. bw = new BufferedWriter(new OutputStreamWriter(os));
    34. } catch (Exception e) {
    35. e.printStackTrace();
    36. }
    37. }
    38. //建立响应协议并发送
    39. public void PushToBrowser(int code, String content) {
    40. if(headInfo == null)
    41. code = 505;
    42. len = content.getBytes(StandardCharsets.UTF_8).length;
    43. buildHead(code);
    44. try{
    45. bw.append(headInfo);
    46. bw.append(content);
    47. bw.flush();
    48. } catch (IOException e) {
    49. e.printStackTrace();
    50. }
    51. }
    52. //建立状态行和响应头
    53. private void buildHead(int code){
    54. headInfo.append("HTTP/1.1").append(BLANK);
    55. headInfo.append(code).append(BLANK);
    56. switch(code){
    57. case 200:
    58. headInfo.append("OK").append(CRLF);
    59. break;
    60. case 404:
    61. headInfo.append("NOT FOUND").append(CRLF);
    62. break;
    63. case 505:
    64. headInfo.append("SERVER ERROR").append(CRLF);
    65. break;
    66. }
    67. headInfo.append("Date:").append(new Date()).append(CRLF);
    68. headInfo.append("Server:MyServer/0.01;charset:UTF-8").append(CRLF);
    69. headInfo.append("Content-type:text/html").append(CRLF);
    70. headInfo.append("Content-length:").append(len).append(CRLF).append(CRLF);
    71. }
    72. }

    主类,同时用于获取请求参数,并将参数传给响应实现类进行进一步操作

    1. package com.xys.server.basic;
    2. import java.io.InputStream;
    3. import java.net.ServerSocket;
    4. import java.net.Socket;
    5. //主类
    6. public class Main {
    7. private static boolean isRunner = true;
    8. public static void main(String[] args) throws Exception {
    9. ServerSocket s_socket = new ServerSocket(8888);
    10. while(isRunner){
    11. Socket client = s_socket.accept();
    12. System.out.println("------------------------------");
    13. System.out.println("一个客户端建立了连接");
    14. new Thread(new Dispatcher(client)).start();
    15. }
    16. }
    17. //封装内部类:多线程获取不同的请求内容
    18. private static class Dispatcher implements Runnable{
    19. private final Socket client;
    20. public Dispatcher(Socket client){
    21. this.client = client;
    22. }
    23. @Override
    24. public void run() {
    25. try{
    26. SaxAnalyze sa = new SaxAnalyze();
    27. //获取请求协议
    28. InputStream is = client.getInputStream();
    29. byte[] flush = new byte[1024 * 10];
    30. int len = is.read(flush);
    31. String info = new String(flush, 0, len);
    32. //从请求协议中分离URI
    33. int idx1 = info.indexOf("/");
    34. int idx2 = info.indexOf(" ", idx1);
    35. String pattern = info.substring(idx1, idx2);
    36. //从请求协议中分离参数
    37. String parameter = info.substring(info.lastIndexOf("\r\n") + 1);
    38. System.out.println("请求协议:" + "\r\n" + info);
    39. System.out.println("请求的服务为:" + pattern);
    40. //解析xml,并根据获得的URI创建对应的对象,并将获得的参数传给创建的对象
    41. sa.Analyze(pattern, parameter, client);
    42. client.close();
    43. } catch (Exception e) {
    44. e.printStackTrace();
    45. isRunner = false;
    46. }
    47. }
    48. }
    49. }