第13次文章:網路編程——httpserver伺服器的搭建(中)

  • 2019 年 10 月 8 日
  • 筆記

這周的程式碼出了點問題,目前還沒有調試完全,就先不把所有程式碼都粘貼上來了。下周默默的去調程式碼了!

對於上周的一個總結:

在上周我們搭建的伺服器,僅僅具有一個傳輸一個介面的功能。在實際應用中,對於同一份資源的訪問,往往可以通過多條路徑進行訪問,而且可以處理多個不同的請求。這些需求,就導致了我們在搭建伺服器的時候,需要逐步去完善伺服器的功能。

一、對於多請求的處理——多態

我們模擬一個具有註冊和登錄功能的網頁,首先我們定義一個抽象類Servlet,為後面的具體功能實現提供一個規範的編寫方法,然後再創建具體的註冊和登錄類,實現網頁多請求處理,也就是多態。

第一步:創建抽象類Servlet

package com.peng.server.demo03;  /**   * 抽象為一個父類   */  public abstract class Servlet {    public void service(Response rep,Request req) throws Exception{      this.doGet(rep,req);      this.doPost(rep,req);    }    public abstract void doGet(Response rep,Request req) throws Exception;    public abstract void doPost(Response rep,Request req) throws Exception;  }

第二步:創建登錄類

package com.peng.server.demo03;  /**   * 登錄介面   */  public class LoginServlet extends Servlet {    @Override    public void doGet(Response rep, Request req) 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("peng") && pwd.equals("123456");    }    @Override    public void doPost(Response rep, Request req) throws Exception {      // TODO Auto-generated method stub    }  }

第三步:創建註冊類

package com.peng.server.demo03;  public class RegisterServlet extends Servlet{    @Override    public void doGet(Response rep, Request req) throws Exception {      // TODO Auto-generated method stub    }    @Override    public void doPost(Response rep, Request req) throws Exception {      rep.println("<html><head><title>返回註冊</title>");      rep.println("</head><body>");      rep.println("你的用戶名為:"+req.getParameter("uname"));      rep.println("</body></html>");    }  }

二、多條路徑訪問同一份資源

在實際情況下,對於同一份資源或者說文件的訪問,往往具有多條路徑可以指向目的地,所以我們需要解決多條路徑訪問同一份資源的問題。我們可以設想一下相關的解決方案,為了滿足多條路徑訪問同一份資源,簡單的思路就是多對一的解決方案。所以我們可以使用容器來解決這個問題,在容器的選擇上,我們使用鍵值對map來存儲目標資源和相關的所有路徑,只要在鍵值對存在著相關路徑,那麼我們就可以在鍵值對map上進行搜索,然後將鍵值對中對應的鍵取出來,與其相對應的值進行比對。具體實現如下:

第一步:建立一個上下文,由於我們所有的網頁資訊均在Servlet中進行實現,所以,我們首先使用一個map為每個多態類別起一個別名,在後面的存儲的時候,方便直接存儲字元串,而不是存儲整個類。然後再使用一個map,用來存儲不同路徑與Servlet類別的映射關係。根據每一條不同的路徑,直接進行映射到同一份資源處。

package com.peng.server.demo03;    import java.util.HashMap;  import java.util.Map;    /**   * 上下文   */  public class ServletContext {    /**     * 使用兩個map的主要作用在於:可以對於同一份資源LoginServlet,首先為其取一個別名,     * 然後可以使用多個路徑訪(/log --->login ||   /login --->login)問同一份資源LoginServlet     */    //為每一個servlet取一個別名    //login ---> com.peng.server.LoginServlet    private Map<String,String> servlet;    // mapping作為一個映射,對於多個訪問路徑,指向同一份資源    //  url --->login    //    /log --->login    //    /login --->login    private Map<String,String> mapping;    public ServletContext() {      servlet = new HashMap<String,String>();      mapping = new HashMap<String,String>();    }      //set和get方法    public Map<String, String> getServlet() {      return servlet;    }    public void setServlet(Map<String, String> servlet) {      this.servlet = servlet;    }    public Map<String, String> getMapping() {      return mapping;    }    public void setMapping(Map<String, String> mapping) {      this.mapping = mapping;    }  }

第二步:將關於登錄和註冊的路徑存儲在相關的map中,比如對於路徑 : /login ,將訪問: /login;對於路徑 : /log ,將訪問: /login;對於路徑: /reg ,將訪問: /register。

package com.peng.server.demo03;  import java.util.Map;  public class WebApp {    private static ServletContext context;    static {      context = new ServletContext();      Map<String,String> mapping = context.getMapping();      /**       * 傳送路徑:       * 對於路徑 : /login  ,將訪問: /login       * 對於路徑 : /log  ,將訪問: /login       * 對於路徑: /reg  ,將訪問: /register       */      mapping.put("/login", "/login");      mapping.put("/log", "/login");      mapping.put("/reg", "/register");      Map<String,String> servlet = context.getServlet();      servlet.put("login","com.peng.server.LoginServlet");      servlet.put("register","com.peng.server.RegisterServlet");    }    //使用了反射的動態屬性    public static Servlet getServlet(String url) throws InstantiationException, IllegalAccessException, ClassNotFoundException {      if(null == url || (url=url.trim()).equals("")) {        return null;      }      //根據字元串(完整路徑)創建對象      String name = context.getServlet().get(context.getMapping().get(url));      return (Servlet)Class.forName(name).newInstance();//在動態使用的時候創建這個對象 確保空構造存在    }  }

三、分發器的封裝

由於整個網頁的請求服務屬於多執行緒處理,所以我們需要建立一個分發器,來進行對各個用戶的請求與應答進行相關的處理。

package com.peng.server.demo03;    import java.io.IOException;  import java.net.Socket;  import com.peng.server.util.CloseUtil;    public class Dispatcher implements Runnable{      private Socket client;    private Response rep;    private Request req;    private int code =200;      public Dispatcher(Socket client) {      this.client = client;      try {        req = new Request(client.getInputStream());        rep = new Response(client.getOutputStream());      } catch (IOException e) {        code = 500;//報錯後,將程式碼狀態推送1個500出去,表示程式碼有問題        return ;      }    }      @Override    public void run() {      try {        Servlet serv = WebApp.getServlet(req.getUrl());//動態的通過訪問地址獲取訪問相關資源        if(null == serv) {          this.code = 404; //找不到處理        }else {          serv.service(rep, req);        }        rep.pushToClient(code);      } catch (Exception e) {        //若此處報錯,則繼續嘗試推送一個500出去        this.code = 500;      }      try {        rep.pushToClient(500);      } catch (IOException e) {        // TODO Auto-generated catch block        e.printStackTrace();      }      CloseUtil.closeAll(client);    }  }

四、關於反射的一點概念:

在我們將多條路徑和目的地資源寫入到map中時,我們使用了一點反射的概念。下面簡單介紹一些關於反射的東西。

1、動態語言:在運行期間隨意的改變對象的類型 +結構 例如:ruby js

java不是動態語言,但具有動態屬性(反射)

2、反射 鏡子

1)在編譯器創建對象 new 完整類名 反射---->通過對象 找出類名

2)在運行期動態的創建對象 分析對象(屬性、方法)

3)jvm在創建對象時自動生成與之對應Class對象,同一個類的多個對象在jvm只有一個與之對應的class對象

3、獲取Class類別的幾種常用方法

1)、可以看成類的元數據。每一個對象在創建的時候,jvm會自動生成一個與之對應的class。同類型的對象對應一個class

2)、獲取class對象的三種方式

a、Object中的getClass方法 : 對象.getClass()

b、類.class形式 : 如student.class.

c、最常用,Class類的靜態方法 :Class.forName("完整類名")


Tags: