解决了几个问题:
    1.返回的响应信息可能是多行展示的,我们可以在传回的响应信息中用
    代表\n,展示的时候通过replace方法将用\n代替


    2.可以将响应信息直接写回,当响应信息内容较多时,也可以将响应信息放在根目录的专门装载信息的文件下,然后直接去读,就不用一遍一遍地拼接,返回

    3.我们返回的响应信息展示的时候,很多时候不是单纯地展示,比如登录界面,会有输入账号密码的过程。我们可以在存响应信息的文件中放置一些特定的符号标记,代表动作,比如 请输入账号

    所以我们解析信息的方法也对应修改

    4.在解析信息时可能会遇到标签form,这几乎意味着我们的请求中包含了新的请求,我们需要将其剥离出来再次解析发送**

    1. package browser;
    2. import java.io.*;
    3. import java.net.Socket;
    4. import java.util.HashMap;
    5. import java.util.Iterator;
    6. import java.util.Scanner;
    7. import java.util.logging.Handler;
    8. public class Browser {
    9. private Scanner input=new Scanner(System.in);
    10. private Socket socket=null;
    11. private String ip;
    12. private int port;
    13. public void openBrowser(){//模拟打开浏览器
    14. //输入一个URL统一资源定位符 ip:port/资源名?key=value&key=value
    15. System.out.println("URL:");
    16. String url=input.nextLine();
    17. this.parseURL(url);
    18. }
    19. private void parseURL(String url){//解析URL,拆成三段
    20. int colonIndex=url.indexOf(":");
    21. int slashIndex=url.indexOf("/");
    22. ip=url.substring(0,colonIndex);//获取ip地址
    23. port=Integer.parseInt(url.substring(colonIndex+1,slashIndex));//获取port
    24. String contentAndParams=url.substring(slashIndex+1);
    25. this.createSocketAndSendRequest(ip,port,contentAndParams);
    26. }
    27. //创建一个socket,将contentAndParams发送给服务器
    28. private void createSocketAndSendRequest(String ip,int port,String contentAndParams){
    29. try {
    30. //通过ip和port创建一个socket
    31. socket=new Socket(ip,port);
    32. //将contentAndParams发送出去(给服务器)
    33. PrintWriter out=new PrintWriter(socket.getOutputStream());//需要一个高级流来发送
    34. out.println(contentAndParams);
    35. out.flush();
    36. //浏览器等待响应消息
    37. this.receiveResponseContent();
    38. }catch (Exception e){
    39. e.printStackTrace();
    40. }
    41. }
    42. //设计一个方法,接收服务器会写的响应信息
    43. private void receiveResponseContent(){
    44. InputStream is = null;//最基本的字节流
    45. try {
    46. is = socket.getInputStream();
    47. InputStreamReader isr = new InputStreamReader(is);//将字节流转化为字符流
    48. BufferedReader br = new BufferedReader(isr);//包装成高级流,可以读取一行
    49. String responseContent=br.readLine();
    50. //解析响应信息并展示
    51. this.parseResponseControllerAndShow(responseContent);
    52. } catch (IOException e) {
    53. e.printStackTrace();
    54. }
    55. }
    56. //设计一个方法,解析响应信息并展示
    57. private void parseResponseControllerAndShow(String responseContent){
    58. String content=null;
    59. HashMap<String,String> paramsMap=null;
    60. responseContent=responseContent.replace("<br>","\n");
    61. //解析其他标签
    62. while (true) {
    63. int lessThanIndex=responseContent.indexOf("<");
    64. int greaterIndex=responseContent.indexOf(">");
    65. if(lessThanIndex!=-1&&greaterIndex!=-1&&lessThanIndex<greaterIndex){
    66. System.out.println(responseContent.substring(0,lessThanIndex));
    67. //分析标签是什么类型,做相应的处理
    68. String tag=responseContent.substring(lessThanIndex,greaterIndex+1);
    69. if(tag.contains("input")){
    70. String value=input.nextLine();
    71. if(paramsMap==null){
    72. paramsMap=new HashMap<>();
    73. }
    74. String[] str=tag.split(" ");
    75. for(String keyAndValues: str){
    76. if(keyAndValues.contains("=")){
    77. String[] keys=keyAndValues.split("=");
    78. if("name".equals(keys[0]))
    79. paramsMap.put(keys[1].substring(1,keys[1].length()-1),value);
    80. }
    81. }
    82. responseContent=responseContent.substring(greaterIndex+1);
    83. }else if(tag.contains("form")){
    84. if(paramsMap==null){
    85. paramsMap=new HashMap<>();
    86. }
    87. String[] str=tag.split(" ");
    88. for(String keyAndValues: str){
    89. if(keyAndValues.contains("=")){
    90. String[] keys=keyAndValues.split("=");
    91. if("action".equals(keys[0]))
    92. content=keys[1].substring(1, keys[1].length()-1);
    93. }
    94. }
    95. responseContent=responseContent.substring(greaterIndex+1);
    96. }
    97. }else{//没有其他标签
    98. System.out.println(responseContent);
    99. break;
    100. }
    101. }
    102. //至此将所有响应信息解析完毕,但如果在标签中遇到form标签,表示又有新的请求
    103. this.sendNewRequest(content,paramsMap);
    104. }
    105. private void sendNewRequest(String content,HashMap<String,String> paramsMap){
    106. if(content!=null){
    107. StringBuilder url=new StringBuilder(ip);
    108. url.append(":");
    109. url.append(port);
    110. url.append("/");
    111. url.append(content);
    112. if(paramsMap!=null){//证明新请求还有参数
    113. url.append("?");
    114. Iterator<String> iterator=paramsMap.keySet().iterator();
    115. while (iterator.hasNext()){
    116. String key=iterator.next();
    117. String value=paramsMap.get(key);
    118. url.append(key);
    119. url.append("=");
    120. url.append(value);
    121. url.append("&");
    122. }
    123. //循环结束后多一个&,我们需要将其去除
    124. url.delete(url.length()-1,url.length());
    125. }
    126. //发送信息给服务器
    127. this.parseURL(url.toString());
    128. }
    129. }
    130. }
    package test;
    
    import browser.Browser;
    
    public class TestMain {
        public static void main(String[] args){
            new Browser().openBrowser();
        }
    }
    
    package controller;
    
    import com.sun.net.httpserver.HttpsServer;
    import server.HttpServlet;
    import server.HttpServletRequest;
    import server.HttpServletResponse;
    
    public class IndexController extends HttpServlet {
    
        public void service(HttpServletRequest hsr, HttpServletResponse response) {
            //传入的参数也是用于携带返回的信息的参数,例如之前学到的流,传入数组,返回值也在数组里
            //1.获取请求发送过来携带的参数(可能没有)
            System.out.println("控制层工作了");
            response.sendRedirect("index.view");
            //2.找到某一个业务层做事
            //3.将最终业务层执行完毕的结果交还给服务器,服务器再将结果返回浏览器
        }
    }
    
    package controller;
    
    import server.HttpServlet;
    import server.HttpServletRequest;
    import server.HttpServletResponse;
    
    public class LoginController extends HttpServlet {
        public void service(HttpServletRequest hsr, HttpServletResponse response) {
            String name= hsr.getParameter("name");//直接获取整个集合不如只获取我们需要的元素好
            String password= hsr.getParameter("password");
            System.out.println(name+"--------"+password);
        }
    }
    
    package server;
    
    
    public abstract class HttpServlet {//为了定义一个规则,方便服务器寻找,方法名字和方法参数必须统一
        public abstract void service(HttpServletRequest hsr,HttpServletResponse response);
    }
    
    package server;
    
    import java.util.HashMap;
    
    public class HttpServletRequest {//为了存储浏览器发送请求时携带的所有信息
        private String content;
        private HashMap<String,String>map=new HashMap<>();
    
        public String getParameter(String key){
            return this.map.get(key);
        }
    
        public HttpServletRequest() {
        }
    
        public HttpServletRequest(String content, HashMap<String, String> map) {
            this.content = content;
            this.map = map;
        }
    
        public String getContent() {
            return content;
        }
    
        public HashMap<String, String> getMap() {
            return map;
        }
    
        public void setContent(String content) {
            this.content = content;
        }
    
        public void setMap(HashMap<String, String> map) {
            this.map = map;
        }
    }
    
    package server;
    
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileReader;
    
    public class HttpServletResponse {//用于接收响应回来的结果
    
        private StringBuilder responseContent=new StringBuilder();
        public void write(String str){
            this.responseContent.append(str);
        }
    
        public void sendRedirect(String path){
            try {
                File file=new File("src//file//"+path);
                FileReader fr=new FileReader(file);
                BufferedReader br=new BufferedReader(fr);
                String value=br.readLine();
                while (value!=null){
                    this.responseContent.append(value);
                    value= br.readLine();
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    
        public String getResponseContent(){
            return this.responseContent.toString();
        }
    
    }
    
    package server;
    
    
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class Server {
        public void startServer(){
            try {
                System.out.println("====启动服务====");
                //自己创建服务
                ServerSocket server=new ServerSocket(9999);
                while (true){
                    //等待某一个客户端过来连接
                    Socket socket=server.accept();//要想同时很多用户访问,可以用多线程来控制
                    new ServerHandler(socket).start();
                }
    
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    
    package server;
    
    
    import java.io.*;
    import java.net.Socket;
    import java.util.HashMap;
    
    public class ServerHandler extends Thread {
    
        private Socket socket;
    
        public ServerHandler(Socket socket) {
            this.socket = socket;
        }
    
        public void run() {
            this.receiveRequest();
        }
    
        private void receiveRequest() {//读取消息
            try {
                InputStream is = socket.getInputStream();//最基本的字节流
                InputStreamReader isr = new InputStreamReader(is);//将字节流转化为字符流
                BufferedReader br = new BufferedReader(isr);//包装成高级流,可以读取一行
                //读取消息  资源名?key=value&key=value
                String contentAndParams = br.readLine();
                //调用一个方法解析读取过来的信息
                this.parseContentAndParams(contentAndParams);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        private void parseContentAndParams(String contentAndParams) {//解析
            //资源名?key=value&key=value
            HashMap<String, String> map = new HashMap<>();
            String content = null;
            int questionMarkIndex = contentAndParams.indexOf("?");
            if (questionMarkIndex != -1) {
                content = contentAndParams.substring(0, questionMarkIndex);
                String params = contentAndParams.substring(contentAndParams.indexOf("?") + 1);
                String[] kv = params.split("&");
    
                for (String str : kv) {
                    String[] values = str.split("=");
                    map.put(values[0], values[1]);
                }
            } else {
                //没有携带参数,请求发过来的信息就是完整的资源名
                content = contentAndParams;
            }
            //自己创建两个对象,一个用于包含所有携带的信息,一个用于接收响应回来的结果
            HttpServletRequest hsr=new HttpServletRequest(content,map);
            HttpServletResponse response=new HttpServletResponse();//空的
            ServletController.findController(hsr,response);//执行完毕后response中就有响应的信息了
            this.responseToBrowser(response);
        }
    
    
        private void responseToBrowser(HttpServletResponse response) {//将最终响应信息写回浏览器
            try {
                PrintWriter out=new PrintWriter(socket.getOutputStream());
                out.println(response.getResponseContent());
                out.flush();
                //浏览器接收响应信息
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    
    }
    
    package server;
    
    import java.io.FileReader;
    import java.io.IOException;
    import java.lang.reflect.Method;
    import java.util.Enumeration;
    import java.util.HashMap;
    import java.util.Properties;
    
    public class ServletController { //用于管理findController方法
    
        //添加一个集合,存储被管理的所有Controller类对象
        private static HashMap<String,HttpServlet>controllerObjectMap=new HashMap<>();//延迟加载
    
        //2.每次找寻控制层的某个类都需要读取文件,性能较低,可以建立缓存机制
        private static HashMap<String,String>controllerNameMap=new HashMap<>();//延迟加载
        static {
            Properties properties=new Properties();
            try {
                properties.load(new FileReader("src//web.properties"));
                Enumeration enumeration=properties.propertyNames();
                while(enumeration.hasMoreElements()){
                    String content=(String)enumeration.nextElement();
                    String realControllerName=properties.getProperty(content);
                    controllerNameMap.put(content,realControllerName);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    
        //1.之所以抽离出来单独成为一个类是因为findController在ServerHandler中做的事与该类不符
        public static void findController(HttpServletRequest hsr,HttpServletResponse response) {//找人做事---控制层
            String content=hsr.getContent();
            try {
                //获取request对象中的请求名字(简单名,我们在内存中已经将简单名与类全名对应存好)
                HttpServlet controllerObject=controllerObjectMap.get(content);
                if(controllerObject==null){
                    //参考配置文件(缓存)
                    String realControllerName=controllerNameMap.get(content);
                    if(realControllerName!=null){
                        //反射获取类
                        Class clazz=Class.forName(realControllerName);
                        controllerObject=(HttpServlet)clazz.newInstance();
                        controllerObjectMap.put(content,controllerObject);
                    }
                }
                Class controllerClass=controllerObject.getClass();
                Method method= controllerClass.getMethod("service",HttpServletRequest.class,HttpServletResponse.class);
                method.invoke(controllerObject,hsr,response);
            }catch (ClassNotFoundException e){
                response.write("请求的"+content+"Controller不存在");
            }catch (NoSuchMethodException e){
                response.write("405 没有可以执行的方法");
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    
        //3.找寻Controller类是为了执行对应方法,所以我们让类中的方法有统一规则,便于查找使用
    
        //4.发现Controller类与之前的Service、Dao类很像,几乎只有方法执行,没什么属性,所以我们可以让其变为单例模式
    }
    
    package test;
    
    import server.Server;
    
    public class TestMain {
        public static void main(String[] args){
            new Server().startServer();
        }
    }
    

    image.png
    image.png QQ录屏20210313134307.mp4 (8.55MB)