java 手写服务器httpserver_203_多请求处理_多态_练习(新增方法越来越多,相互关联)

// 1.-----------------------------------------
package 手写服务器httpserver_203_多请求处理_多态_练习;

import java.io.IOException;
import java.net.ServerSocket;

/**

  • 创建服务器并启动

  • 1.请求

  • 2.响应 :在接收客户端
    */
    public class Server {//Server 服务器
    private ServerSocket server;
    //定义一个字符串常量 便于后面使用
    public static final String CRLF = “\r\n”;//final最终的;CRLF换行符
    //定义空格便于后面使用
    public static final String BLANK = " ";//BLANK空格

    private boolean isShutDown = false;//isShutDown判断线程池是否已关闭
    public static void main(String[] args) {
    Server server = new Server();
    server.start();//调用
    }
    /**

    • 创建一个方法 便于使用:启动
      /
      public void start(){//start启动
      start(8888);//调用
      }
      /
      *

    • 指定端口方法
      /
      public void start(int port){//start启动;port端口
      try {
      server = new ServerSocket(port);
      this.receive();//调用;启动时就准备接收
      } catch (IOException e) {
      //e.printStackTrace();异常没必要报出
      stop();
      }
      }
      /
      *

    • 接收客户端
      */
      private void receive(){//receive接收
      try {//对代码在Servlet类进行了封装
      while(!isShutDown){//只要出错就证明服务器有问题
      new Thread(new Dispatcher(server.accept())).start();//Thread线程;start启动
      }

      } catch (IOException e) {
      //e.printStackTrace();
      stop();//调用
      }
      }
      /**

    • 停止服务器
      */
      public void stop(){//stop停止
      isShutDown = true;//如果服务器有问题;就变成true
      CloseUtil.closeSocket(server); //关闭服务器
      }
      }

//-2. —封装响应信息------------------------------------
package 手写服务器httpserver_203_多请求处理_多态_练习;

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响应
    */
    public class Response {
    //定义一个字符串常量 便于后面使用
    public static final String CRLF = “\r\n”;//final最终的;CRLF换行符
    //定义空格便于后面使用
    public static final String BLANK = " ";//BLANK空格
    //推送流
    private BufferedWriter bw;
    //正文
    private StringBuilder content;//StringBuilder可变的字符序列;content内容
    //存储头信息
    private StringBuilder headInfo;//StringBuilder可变的字符序列;headInfo头信息
    //存储正文长度
    private int len = 0;
    //构造器
    public Response() {
    headInfo = new StringBuilder();
    content = new StringBuilder();
    //重新构建 正文长度有可能是变化的
    len = 0;
    }

    public Response(OutputStream os) {
    this();//构造器的相互调用
    bw = new BufferedWriter(
    new OutputStreamWriter(os));
    }

    public Response(Socket client) {
    this();//构造器的相互调用
    try {
    bw = new BufferedWriter(
    new OutputStreamWriter(client.getOutputStream()));//处理异常
    } catch (IOException e) {
    headInfo = null;//如果报错 就为空 在下面’推送到客户端’增加一个判断
    }
    }
    /**

    • 正文是外部构建
    • @return
      /
      public Response println(String info){//Response响应
      //不断构建
      content.append(info).append(CRLF);//getBytes
      //长度不断变化
      len+=(info+CRLF).getBytes().length;//getBytes中文字符串;因为CRLF也占位置所以加上
      return this;
      }
      /
      *
    • 构建响应头;内部构建
      /
      private void createHeadInfo(int code){//createHeadInfo创建的头信息;code代码
      //1.HTTP协议版本,状态代码,描述
      //把可变的与不变的区分开; 不变的
      headInfo.append(“HTTP/1.1”).append(BLANK).append(code).append(BLANK);
      //可变的
      switch(code){
      case 200:
      headInfo.append(“OK”);
      break;
      case 404:
      headInfo.append(“NOT FOUND”);//NOT FOUND无法找到
      break;
      case 500:
      headInfo.append(“SEVER ERROR”);//SEVER ERROR服务器错误
      break;
      }
      headInfo.append(CRLF);//加入空格BLANK
      //2.响应头(Response Head)
      headInfo.append(“Server:bjsxt Server/0.0.1”).append(CRLF);//0.0.1版本号
      //日期
      headInfo.append(“Date:”).append(new Date()).append(CRLF);//获取当前时间
      //正文的类型及编码格式
      headInfo.append(“Content-Type:Text/html;charset=gbk”).append(CRLF);//GBK汉字内码扩展规范简体中文
      //处理正文的长度
      headInfo.append(“Content-Length:”).append(len).append(CRLF);
      //分隔符
      headInfo.append(CRLF);//与正文分开的空格区域
      }
      /
      *
    • 推送到客户端方法
    • @throws IOException
      /
      void pushToClient(int code) throws IOException{//pushToClient推动客户
      if(null==headInfo){
      code = 200;
      }
      //构建头
      createHeadInfo(code);
      //头信息+分隔符
      bw.append(headInfo.toString());//处理异常
      //正文
      bw.append(content.toString());
      bw.flush();
      }
      /
      *
    • 关闭
      */
      public void close(){
      CloseUtil.closeIO(bw);
      }
      }

//-----3. --请求Request--------------------------------------
package 手写服务器httpserver_203_多请求处理_多态_练习;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

/**

  • 请求Request 封装
    /
    public class Request {
    //请求方式
    private String method;//method方法
    private String url;//请求资源
    //请求参数:Map映射;List目录,可能是多个;parameterMapValues参数映射值
    private Map<String,List> parameterMapValues;
    //内部常量
    //定义一个字符串常量 便于后面使用
    public static final String CRLF = “\r\n”;//final最终的;CRLF换行符
    //定义空格便于后面使用
    public static final String BLANK = " “;//BLANK空格
    private InputStream is;//输入流
    //请求信息
    private String requestInfo;//requestInfo请求信息
    public Request() {//空参
    //初始化 避免空指针异常
    method = “”;
    url = “”;
    //在界面获取的都是字符串 ,以后需要什么类型在转
    parameterMapValues = new HashMap<String,List>();
    requestInfo = “”;
    }
    public Request(InputStream is) {
    this();
    this.is = is;
    try {
    byte[] data = new byte[20480];//字节数组
    int len = is.read(data);//内部使用
    requestInfo = new String(data,0,len); //构建
    } catch (IOException e) {
    return ;//如果报错 就停止
    }
    parseRequestInfo();//分析头信息
    }
    //创建方法;分析请求信息 即分解字符串
    private void parseRequestInfo(){//parseRequestInfo解析请求信息
    //如果为空或者 遇到空格就停止
    if(null==requestInfo||(requestInfo=requestInfo.trim()).equals(”")){
    return;
    }
    /
    *
    * ================================================
    *1.从信息的首行分解出:请求方式,请求路径,请求参数 (get可能存在)
    * 如:GET /index.html?uname=aaa&pwd=1234567 HTTP/1.1
    *2.如果为post方式,请求参数可能在正文最后中
    * 思路:1.请求方法:找出第一个/截取即可 2.请求资源:找出第一个/HTTP
    */
    String paramString = “”;//接收请求参数;paramString参数的字符串
    //获取请求方式 firstLine第一行;substring子字符串;indexOf 查找字符或者子串第一次出现的地方
    String firstLine = requestInfo.substring(0,requestInfo.indexOf(CRLF));//获取第一行0至换行
    //获取第一行首位字符串
    int idx = requestInfo.indexOf("/");//记录’/‘的位置
    this.method = firstLine.substring(0,idx).trim();//trim修剪,整理
    String urlstr = firstLine.substring(idx,firstLine.indexOf(“HTTP/”)).trim();//indexOf 查找字符或者子串第一次出现的地方
    //这里只判断2种方式
    if(this.method.equalsIgnoreCase(“post”)){//IgnoreCase忽略大小写
    this.url = urlstr;
    paramString = requestInfo.substring(requestInfo.lastIndexOf(CRLF)).trim();//lastIndexOf 查找字符或者子串是最后一次出现的地方
    }else if(this.method.equalsIgnoreCase(“get”)){
    if(urlstr.contains("?")){//是否存在参数;contains包含
    //如果存在 就要分割数组
    String[] urlArray = urlstr.split("\?");//如果使用split’;’?'是特殊符号需要加\
    this.url = urlArray[0];//开始位置
    paramString = urlArray[1];//接收请求参数

     	}else{
     		this.url = urlstr;
     	}
     }
     //判断
     if(paramString.equals("")){//不存在请求参数   
     	return;
     }
     //如果从存在就封装到Map里面
     parseParams(paramString);//这个方法不是任何时候都调用,只有paramString =  urlArray[1];存在时才调用	  
    

    }
    //将 请求参数封装到Map中 好处是快速获取

    private void parseParams(String paramString){//parseParams解析参数;paramString参数的字符串
    //分割;将字符串转成数组;StringTokenizer字符分解器
    StringTokenizer token = new StringTokenizer(paramString,"&");
    while(token.hasMoreTokens()){//hasMoreTokens得到更多的标记字符串
    String keyValue = token.nextToken();//nextToken下一个标记字符串
    //继续分割
    String[] keyValues = keyValue.split("=");//split分割
    //判断
    if(keyValues.length1){
    keyValues = Arrays.copyOf(keyValues, 2);//数组的拷贝
    keyValues[1] = null;
    }
    //设置键与值
    String key = keyValues[0].trim();
    //这里增加中文解码方法decode();gbk简体中文(GBK)
    String value = null
    keyValues[1]?null:decode(keyValues[1].trim(),“gbk”);//先判断如果等于null就是1即本身;否则就去下空
    //转成Map 如果不存在就放进去
    if(!parameterMapValues.containsKey(key)){//containsKey判断是否包含查找的是键
    parameterMapValues.put(key,new ArrayList());//ArrayList数组列表;put放
    }

     	List<String> values = parameterMapValues.get(key);//拿到容器
     	values.add(value);//加个多个
     }	 
    

    }
    /**

    • 增加 中文解码方法

    • decode解码器
      /
      private String decode(String value, String code){//value值;code代码
      try {
      return java.net.URLDecoder.decode(value,code);//java.net.URLDecoderjavanet URL解码器;(value,code)解码value及编码格式code
      } catch (UnsupportedEncodingException e) {
      //e.printStackTrace();
      }
      return null;
      }
      /
      *

    • 根据页面name 获取对应的多个值

    • @param name

    • @return
      */
      public String[] getParameterValues(String name){

      List values = null;
      if((values=parameterMapValues.get(name))==null){
      return null;
      }else{
      return values.toArray(new String[0]);
      }
      }
      /**

    • 根据页面获取对应的值;这是一个值

    • @param args
      */
      public String getParameter(String name){
      String[] values = getParameterValues (name);
      if(null==values){
      return null;
      }
      return values[0];
      }
      //为了外部获取 url 增加set与get方法
      public String getUrl() {
      return url;
      }
      }

//----4. -------添加服务程序-----------------------------------------------
package 手写服务器httpserver_203_多请求处理_多态_练习;
/**

  • 添加服务程序

*/
public class RegisterServlet extends Servlet{

@Override
public void doGet(Request req,Response rep) throws Exception {
		
} 
/**
 * 下面的方法同上面一致
 */
@Override
public void doPost(Request req,Response rep) throws Exception {
	rep.println("<html><head><title>返回注册</title>");
	rep.println("</head><body>");//获取请求
	rep.println("你的用户名:"+req.getParameter("uname"));
 	rep.println("</body></html>");
	
}

}

//------5. -----Dispatcher调度程序------------------------------------------
package 手写服务器httpserver_203_多请求处理_多态_练习;

import java.io.IOException;
import java.net.Socket;

/**

  • 线程类;Dispatcher调度程序
    *一个请求与相应,就一个此对象
    */
    public class Dispatcher implements Runnable{
    private Socket client;//初始化
    private Request req;
    private Response rep;
    private int code = 200;
    Dispatcher(Socket client) {//构造器
    this.client = client;
    try {
    req = new Request(client.getInputStream());//包装 Server6里的接收客户端req
    rep = new Response(client.getOutputStream());
    } catch (IOException e) {
    //e.printStackTrace();
    code = 500;//如果报错 就显示500
    return;
    }
    }
    //重写
    @Override
    public void run() {

     //发送命令
     try {
     	Servlet serv = WebApp.getServlet(req.getUrl());//多态
     	if(null==serv){
     		this.code = 404;//如果找不到对应处理   就显示404
     	}else {
     		serv.service(req, rep);
     	}
     	rep.pushToClient(code);//pushToClient推动客户
     
     } catch (Exception e) {//两个异常合并一个
     	e.printStackTrace();
     	this.code = 500;//系统内部错误
     } 
     try {
     		rep.pushToClient(500);//如果报错 就显示500
     } catch (IOException e) {
     		e.printStackTrace();  
     }  
     //关闭流
     CloseUtil.closeSocket(client);
    

    }

}

//-----6. ------登录-服务----------------------------------------------
package 手写服务器httpserver_203_多请求处理_多态_练习;
/**

  • LoginServlet登录服务

*/
public class LoginServlet extends Servlet{

@Override
public void doGet(Request req,Response rep) throws Exception {
	String name = req.getParameter("uname");
	String pwd = req.getParameter("pwd");
	if(login(name, pwd)){
		rep.println("登录成功");
	}else{
		rep.println("登录失败");
	}
}
public boolean login(String name,String pwd){
	
	return name.equals("王伟") && pwd.equals("12345");
}
/**
 * 下面的方法同上面一致
 */
@Override
public void doPost(Request req,Response rep) throws Exception {
	
}

}

//----7.----------------------------------------------
package 手写服务器httpserver_203_多请求处理_多态_练习;
/**

  • Servlet控制器

  • 抽象为一个父类
    */
    public abstract class Servlet {//进行抽象
    //封装 ; service服务
    public void service(Request req,Response rep) throws Exception{
    this.doGet(req,rep);//抛出异常
    this.doPost(req,rep);
    }
    public abstract void doGet(Request req,Response rep) throws Exception;//手动抛出异常
    public abstract void doPost(Request req,Response rep) throws Exception;
    }

    // 8.-----上下文----------------------------------------------------------------
    package 手写服务器httpserver_203_多请求处理_多态_练习;

import java.util.HashMap;
import java.util.Map;

/**

  • 上下文
    */
    public class ServletContext {
    //为每一个servlet取个名字
    private Map<String, Servlet> servlet ;
    //一个名字可以多个路径访问;一个资源可以映射多个路径的访问
    private Map<String,String> mapping ;//mapping映射

    ServletContext(){//初始化
    servlet = new HashMap<String, Servlet>();
    mapping = new HashMap<String, String>();
    }

    public Map<String, Servlet> getServlet(){
    return servlet;
    }
    public void setServlet(Map<String, Servlet> servlet) {
    this.servlet = servlet;
    }
    public Map<String, String> getMapping() {
    return mapping;
    }
    public void setMapping( ) {
    this.mapping = mapping;
    }
    }

// 9.---------WebApp-网页应用-------------------------------------
package 手写服务器httpserver_203_多请求处理_多态_练习;
/**

  • 网页应用
    */
    import java.util.Map;

public class WebApp {
private static ServletContext contxt;

static {
	contxt = new ServletContext(); //模拟
	
	Map<String, String> mapping = contxt.getMapping();
	mapping.put("/login", "login"); //模拟数据
	mapping.put("/log", "login");
	mapping.put("/reg", "register");
	
	Map<String, Servlet> servlet = contxt.getServlet(); 
	servlet.put("login", new LoginServlet());//new个子类
	servlet.put("register", new RegisterServlet());
}
public static Servlet getServlet(String url){//通过url获取不同的数据
	if((null==url)||(url.trim()).equals("")){//=空就不用处理
		return null;
	}
	 return contxt.getServlet().get(contxt.getMapping().get(url));//否则就从contxt里面获取url
} 

}

// 10.----------关闭流的方法 ------------------------------------
package 手写服务器httpserver_203_多请求处理_多态_练习;

import java.io.Closeable;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.ServerSocket;
import java.net.Socket;

/**

  • 关闭流的方法
    /
    public class CloseUtil {//CloseUtil关闭所有的
    /
    *

    • 关闭IO流

    • @param io
      /
      /public static void closeIO(Closeable…io){//closeAll全部关闭;Closeable可关闭的参数
      //判断
      for(Closeable temp:io){
      try {
      if(null!=temp){
      temp.close();//处理异常
      }
      } catch (IOException e) {
      e.printStackTrace();
      }
      }
      }
      /
      /
      *

    • 使用泛型实现关闭IO流
      */
      public static void closeIO(T…io){
      for(Closeable temp:io){
      try {
      if(null!=temp){
      temp.close();//处理异常
      }
      } catch (IOException e) {
      e.printStackTrace();
      }
      }
      }
      public static void closeSocket(ServerSocket socket){
      try {
      if(null!=socket){
      socket.close();//处理异常
      }
      } catch (IOException e) {
      e.printStackTrace();
      }
      }
      public static void closeSocket(Socket socket){
      try {
      if(null!=socket){
      socket.close();//处理异常
      }
      } catch (IOException e) {
      e.printStackTrace();
      }
      }
      public static void closeSocket(DatagramSocket socket){

       if(null!=socket){
       	socket.close();//处理异常
       }
      

    }
    }

//结果测试-----------------------------------
1.启动服务器
2. 复制
在这里插入图片描述
3.打开并修改,保存
在这里插入图片描述
4.打开浏览器 输入
在这里插入图片描述
在这里插入图片描述
换个名字 或密码测试
在这里插入图片描述
在这里插入图片描述
测试reg
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值