第一课、请求与相应

1、请求响应原理图

在这里插入图片描述

2、浏览器端示例代码

2.1浏览器类

package browser;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Scanner;

public class Browser1 {
    private Scanner input = new Scanner(System.in);
    private Socket socket;
    private String ip;
    private int port;

    //打开浏览器窗口
    public void openBrowser(){
        //输入一个URL统一资源定位符
        //  ip:port/index.html?key=value&key=value
        //  localhost:9999/controller.IndexController?name=zzt
        //  请求是一个资源名IndexController  反射进行查找
        System.out.print("URL:");
        String url = input.nextLine();
        this.parseURL(url);
    }

    //设计一个方法 解析url
    private void parseURL(String url){
        //找寻冒号和第一个斜杠所在的位置
        int colonIndex = url.indexOf(":");
        int slashIndex = url.indexOf("/");
        //获取IP port contentAndParams
        ip = url.substring(0,colonIndex);
        port = Integer.parseInt(url.substring(colonIndex+1,slashIndex));
        String contentAndParams = url.substring(slashIndex+1);
        this.createSocketAndSendRequest(ip,port,contentAndParams);
    }

    //设计一个方法 创建一个socket 将contentAndParams发送给服务器
    private void createSocketAndSendRequest(String ip,int port,String contentAndParams){
        try {
            //通过ip和port创建一个socket
            socket = new Socket(ip,port);
            //将contentAndParams发送出去(给服务器)
            PrintWriter out = new PrintWriter(socket.getOutputStream());
            out.println(contentAndParams);//    资源名?key=value&key=value
            out.flush();
            //浏览器等待响应信息
            this.receiveResponseContent();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //设计一个方法 接受服务器回写的响应信息
    private void receiveResponseContent(){
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String responseContent = reader.readLine();
            //解析响应信息并展示
            this.parseResponseContentAndShow(responseContent);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //设计一个方法 解析响应信息并展示
    private void parseResponseContentAndShow(String responseContent){
        //创建两个新的变量 用于存储新一次的请求和参数
        String content = null;
        HashMap<String,String> paramsMap = null;

        //解析一个<br>标签
        responseContent = responseContent.replace("<br>","\r\n");
        while(true){
            //解析其他的标签
            int lessThanIndex = responseContent.indexOf("<");
            int greaterThenIndex = responseContent.indexOf(">");
            //如果两个符号成对 证明存在一个有意义的标签
            if(lessThanIndex!=-1 && greaterThenIndex!=-1 && lessThanIndex<greaterThenIndex){
                //输出小于号前面的内容
                System.out.println(responseContent.substring(0,lessThanIndex));
                //分析标签是什么类型 做相应的处理  <input name="" value="">
                String tag = responseContent.substring(lessThanIndex,greaterThenIndex+1);
                if(tag.contains("input")){
                    String value = input.nextLine();
                    if(paramsMap==null){
                        paramsMap = new HashMap<String,String>();
                    }// <input name="xxx" value="">
                    String[] keyAndValues = tag.split(" ");//将一个大的标记按照空格拆分
                    for(String keyAndValue : keyAndValues){//循环每一组键值对
                        if(keyAndValue.contains("=")){//如果当前的一组中包含有等号 证明是正常的参数
                            String[] KV = keyAndValue.split("=");//按照等号拆分
                            if("name".equals(KV[0])){
                                paramsMap.put(KV[1].substring(1,KV[1].length()-1),value);
                            }
                        }
                    }
                }else if(tag.contains("form")){//<form action="" method="">
                    String[] keyAndValues = tag.split(" ");//将一个大的标记按照空格拆分
                    for(String keyAndValue : keyAndValues){//循环每一组键值对
                        if(keyAndValue.contains("=")){//如果当前的一组中包含有等号 证明是正常的参数
                            String[] KV = keyAndValue.split("=");//按照等号拆分
                            if("action".equals(KV[0])){
                                //产生一个新的请求
                                content = KV[1].substring(1,KV[1].length()-1);
                            }
                        }
                    }
                }
                responseContent = responseContent.substring(greaterThenIndex+1);
            }else{//如果符号不成对 证明不存在其他标签
                //则直接输出全部的内容
                System.out.print(responseContent);
                break;
            }
        }
        //------至此将所有的响应信息解析完毕--------------------------------------
        //如果标签中遇到了<form>表示我还有一次新的请求
        this.sendNewRequest(content,paramsMap);
    }

    private void sendNewRequest(String content,HashMap<String,String> paramsMap){
        if(content!=null){//遇到了一个form标签 还需要发送下一次请求
            StringBuilder url = new StringBuilder(ip);
            url.append(":");
            url.append(port);
            url.append("/");
            url.append(content);
            if(paramsMap!=null){//证明新情求还有参数
                url.append("?");
                Iterator<String> it = paramsMap.keySet().iterator();
                while(it.hasNext()){
                    String key = it.next();
                    String value = paramsMap.get(key);
                    url.append(key);
                    url.append("=");
                    url.append(value);
                    url.append("&");
                }
                //循环执行完毕后 最终多了一个&符号 将其删除
                url.delete(url.length()-1,url.length());
            }
            this.parseURL(url.toString());
        }
    }

}

2.2主启动类

package test;

import browser.Browser;
import browser.Browser1;

public class TestMain {

    public static void main(String[] args){
        new Browser1().openBrowser();
    }
}

3、服务器端示例代码

3.1、服务器核心类

package servers;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public void startServer(){
        try {
            System.out.println("====启动服务器====");
            //获取配置文件中的port号码
            int port = Integer.parseInt(ServerFileReader.getValue("port"));
            //自己创建一个服务
            ServerSocket server = new ServerSocket(port);
            while(true){
                //等待某一个客户端过来连接
                Socket socket = server.accept();
                //启动一个线程  负责处理当前浏览器发送过来的消息
                new ServerHandler(socket).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3.2、ServerFileReader读取配置类

package servers;

import java.io.FileReader;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Properties;

public class ServerFileReader {

    //类的目的是在服务器启动的时候
    //读取server.properties的配置文件----->port端口号

    private static HashMap<String,String> map = new HashMap<String,String>();

    static{
        try {
            Properties pro = new Properties();
            pro.load(new FileReader("src/server.properties"));
            Enumeration en = pro.propertyNames();
            while(en.hasMoreElements()){
                String key = (String)en.nextElement();//port
                String value = pro.getProperty(key);
                map.put(key,value);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static String getValue(String key){
        return map.get(key);
    }
}

3.3、server.properties配置文件

port=9999

3.4、ServerHandler创建线程负责处理当前浏览器发送过来的消息

package servers;

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 reader = new BufferedReader(isr);//包装成高级流 可以读取一行
            //读取消息  content?key=value&key=value
            String contentAndParams = reader.readLine();
            //调用一个方法解析读取过来的信息
            this.parseContentAndParams(contentAndParams);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //解析
    private void parseContentAndParams(String contentAndParams){
        //创建两个变量  存储请求的资源名  携带的参数
        String content = null;
        HashMap<String,String> paramsMap = null;
        //  content?key=value&key=value
        //找寻问号所在的位置
        int questionMarkIndex = contentAndParams.indexOf("?");
        //判断是否携带了参数
        if(questionMarkIndex!=-1){
            //携带了参数 截取问号前面的信息-->请求资源名 问号后面的信息拆分存入map集合里
            content = contentAndParams.substring(0,questionMarkIndex);
            paramsMap = new HashMap<String,String>();
            //处理问号后面的参数 拆分存入map集合   key=value&key=value
            String params = contentAndParams.substring(questionMarkIndex+1);
            String[] keyAndValues = params.split("&");
            for(String keyAndValue : keyAndValues){
                String[] KV = keyAndValue.split("=");
                paramsMap.put(KV[0],KV[1]);
            }
        }else{
            //没有携带参数 请求发过来的信息就是完整的资源名
            content = contentAndParams;
        }
        //-----至此将请求发送过来的字符串解析完毕(content,paramsMap)------------------
        //自己创建两个对象
        // 一个是为了包含所有请求携带的信息
        // 另一个是为了接受响应回来的结果  创建时是空对象 在Controller执行完毕后 将其填满
        HttpServletRequest request = new HttpServletRequest(content,paramsMap);
        HttpServletResponse response = new HttpServletResponse();//空的
        ServletController.findController(request,response);
        //上面这个方法执行完毕 真实的Controller里面的那个service方法执行完了
        //response对象中应该就有响应信息啦
        this.responseToBrowser(response);
    }

    //将最终的响应信息 写回浏览器
    private void responseToBrowser(HttpServletResponse response){
        try {
            PrintWriter out = new PrintWriter(socket.getOutputStream());
            out.println(response.getResponseContent());
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3.5、HttpServletRequest(存储浏览器发送请求时携带的所有信息)

package servers;

import java.util.HashMap;

public class HttpServletRequest {
    //自己创建的一个类型
    //目的是为了存储  浏览器发送请求时携带的所有信息

    private String content;
    private HashMap<String,String> paramsMap;

    public HttpServletRequest() {}
    public HttpServletRequest(String content, HashMap<String, String> paramsMap) {
        this.content = content;
        this.paramsMap = paramsMap;
    }

    public String getParameter(String key){
        return this.paramsMap.get(key);
    }

    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
    public void setParamsMap(HashMap<String, String> paramsMap) {
        this.paramsMap = paramsMap;
    }
}

3.6、HttpServletResponse(存储响应信息)

package servers;

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);
    }
    //让response读取一个文件  文件中的内容是响应信息
    public void sendRedirect(String path){
        try {
            File file = new File("src//file//"+path);
            BufferedReader reader = new BufferedReader(new FileReader(file));
            String value = reader.readLine();
            while(value!=null){
                this.responseContent.append(value);
                value = reader.readLine();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public String getResponseContent(){
        return this.responseContent.toString();
    }
}

3.7、读取响应信息为:

****************<br>
****银行系统****<br>
****************<br>
<form action="login" method="">
请输入账号<input name="name" value=""><br>
请输入密码<input name="password" value=""><br>

3.8 、定义统一规则

package servers;
//出现的目的是为了定义一个规则
public abstract class HttpServlet {
    public abstract void service(HttpServletRequest request, HttpServletResponse response);

}

3.9 主界面控制层

package controller;

import servers.HttpServlet;
import servers.HttpServletRequest;
import servers.HttpServletResponse;

public class IndexController extends HttpServlet {
    //一个控制层

    public void service(HttpServletRequest request, HttpServletResponse response){
        //1.获取请求发送过来携带的参数?
        //2.找到某一个业务层的方法 做事
        //3.将最终业务层执行完毕的结果 交还给服务器 让服务器写回给浏览器
        response.sendRedirect("index.view");
    }

}

4.0.登录控制层

package controller;

import servers.HttpServlet;
import servers.HttpServletRequest;
import servers.HttpServletResponse;

public class LoginController extends HttpServlet {

    public void service(HttpServletRequest request, HttpServletResponse response) {
        String name = request.getParameter("name");
        String password = request.getParameter("password");

        System.out.println(name+"---"+password);
    }
}

4.1、ServletController (参考web.properties文件读取具体Controller并提供缓存)

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;

//这个类的目的是为了管理  findController方法
//1.方法与之前服务器Handler做的事情不一致   抽离出来
//2.每一次找寻Controller类的时候都需要参考一下web.properties
//      读取文件性能会比较低  增加一个缓存机制
//3.每一个Controller类都是由findController方法来找寻
//      找到了Controller类的目的是为了执行里面的方法
//      让类中的方法有一个统一的规则----便于查找和使用
//4.发现Controller类与之前的Service和Dao相似  只有方法执行  没有属性
//      让Controller类的对象变成单例模式

public class ServletController {

    //添加一个缓存 用来存储web.properties配置文件中的信息(请求名字=真实Controller类名)
    private static HashMap<String,String> controllerNameMap = new HashMap<>();
    //添加一个集合 存储被管理的所有Controller类对象
    private static HashMap<String,HttpServlet> controllerObjectMap = new HashMap<>();
    //延迟加载对象的方式

    //创建一个静态块 在当前类加载的时候将配置文件中的所有信息读取出来存入缓存集合中
    static{
        try {
            Properties pro = new Properties();
            pro.load(new FileReader("src//web.properties"));
            Enumeration en = pro.propertyNames();
            while(en.hasMoreElements()){
                String content = (String)en.nextElement();
                String realControllerName = pro.getProperty(content);
                controllerNameMap.put(content,realControllerName);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //找人干活---控制层    (controller  action  servlet)
    //content----index     map-----{{name,zzt},{},{}}
    public static void findController(HttpServletRequest request,HttpServletResponse response){
        //获取request对象中的请求名字
        String content = request.getContent();
        try {
            //先去objectMap中找寻需要的对象
            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);
                }
            }
            //----以上可以确保controllerObject对象肯定存在-------------
            //反射找寻类中的方法
            Class controllerClass = controllerObject.getClass();
            Method serviceMethod = controllerClass.getMethod("service",HttpServletRequest.class,HttpServletResponse.class);
            serviceMethod.invoke(controllerObject,request,response);
        } catch (ClassNotFoundException e) {
            response.write("请求的"+content+"Controller不存在");
        } catch (NoSuchMethodException e){
            response.write("405 没有可以执行的方法");
        } catch (Exception e){
            e.printStackTrace();
        }
    }

}

4.2、web.properties配置文件如下

index=controller.IndexController
login=controller.LoginController
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值