第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("完整類名")