?Servlet 體系結構的類層次關系
- Servlet(接口):定義了 Servlet 的核心生命周期方法(如?
init()
、service()
、destroy()
),是所有 Servlet 的頂層規范,任何 Servlet 都需實現該接口。 - GenericServlet(抽象類):實現了?
Servlet
?接口,提供了與協議無關的通用功能(如初始化、銷毀等),但未具體處理 HTTP 協議相關邏輯,保留抽象方法。 - HttpServlet(抽象類):繼承?
GenericServlet
,針對 HTTP 協議進行擴展,實現了基于 HTTP 方法(如 GET、POST)的請求分發邏輯(如?doGet()
、doPost()
?等方法),專注于處理 HTTP 場景。 - 自定義 Servlet 類:通過繼承?
HttpServlet
,重寫對應的方法(如?doGet()
、doPost()
),實現具體的業務邏輯,以處理客戶端的 HTTP 請求。
Servlet資源準備
1、Servlet接口:
- Servlet接口的作用:實現Servlet生命周期,定義init()、service(request,response)和destroy()方法。
- 接口中的方法全都是抽象方法,只定義不實現,方法頭默認為public abstract。
import com.qcby.Servlet.req.HttpServletRequest;
import com.qcby.Servlet.req.HttpServletResponse;public interface Servlet {void init();void service(HttpServletRequest request, HttpServletResponse response) throws Exception;void destroy();
}
-
init()
?方法- 功能:初始化 Servlet 實例。
- 調用時機:Servlet 容器(如 Tomcat)創建 Servlet 實例后立即調用,通常用于資源初始化(如加載配置文件、建立數據庫連接等)。
- 注意事項:該方法在 Servlet 的生命周期中僅調用一次。
-
service(HttpServletRequest request, HttpServletResponse response)
?方法- 功能:處理客戶端請求并返回響應。
- 參數:
HttpServletRequest
:封裝客戶端請求信息(如請求頭、請求參數、URL 等)。HttpServletResponse
:封裝服務器響應信息(如狀態碼、響應頭、響應體等)。
- 異常處理:聲明拋出?
Exception
,允許實現類處理各類異常(如 IO 異常、業務異常等)。 - 調用時機:每次接收到客戶端請求時,容器會調用該方法。
-
destroy()
?方法- 功能:銷毀 Servlet 實例。
- 調用時機:Servlet 容器關閉或重新加載 Servlet 時調用,通常用于資源釋放(如關閉數據庫連接、釋放文件句柄等)。
- 注意事項:該方法在 Servlet 的生命周期中僅調用一次。
2、GenericServlet抽象類
- ?GenericServlet抽象類實現Servlet接口中的init()方法和destroy()方法。
為什么GenericServlet類是抽象類?
? 我們知道當一個普通類繼承一個接口時,需要對接口中的所有方法進行實現,但這里我們不實現Servlet接口中的service(request,response)方法,因此我們需要把GenericServlet類變成抽象類,因為抽象類不必實現接口中的全部方法。
public abstract class GenericServlet implements Servlet {public void init(){System.out.println("初始化成功");}public void destroy(){System.out.println("銷毀成功");}
}
3、HttpServlet抽象類
HttpServlet抽象類實現Servlet接口中的service(request,response)方法
為什么HttpServlet類也是抽象類?
? 之所以HttpServlet類也是抽象類,是因為普通類中必須對方法進行實現,而在HttpServlet類中,實現service(request,response)方法的邏輯為:如果接收到GET請求,那么執行doGet方法;如果接收到POST請求,那么執行doPost方法。在此我們只需要對doGet和doPost方法進行定義不需要實現,所以HttpServlet只有成為抽象類,才不必實現doGet和doPost方法。
import com.qcby.Servlet.req.HttpServletRequest;
import com.qcby.Servlet.req.HttpServletResponse;public abstract class HttpServlet extends GenericServlet {//實現Servlet生命周期的service方法public void service(HttpServletRequest request, HttpServletResponse response) throws Exception {//如果接收到GET請求,那么執行doGet方法if(request.getMethod().equals("GET")){doGet(request,response);}//如果接收到POST請求,那么執行doPost方法else if(request.getMethod().equals("POST")){doPost(request,response);}}protected abstract void doGet(HttpServletRequest request,HttpServletResponse response) throws Exception;protected abstract void doPost(HttpServletRequest request,HttpServletResponse response)throws Exception;
}
4、HttpRequest類
- HttpRequest類實現請求路徑和請求方法的獲取與輸出。
public class HttpServletRequest {private String path;private String method;public String getPath(){return path;};public void setPath(String path){this.path = path;}public String getMethod(){return method;}public void setMethod(String method){this.method = method;}
}
5、HttpResponse類
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;public class HttpServletResponse {private OutputStream outputStream;public HttpServletResponse(OutputStream outputStream){this.outputStream = outputStream;}public void writeServlet(String context) throws IOException{outputStream.write(context.getBytes());}
}
I/0請求
?
? 客戶端向瀏覽器發出的請求,瀏覽器不會主動向客戶端發送請求
1、請求方式
? HTTP協議的請求方式有很多,在這里我們只介紹GET請求和POST請求。
(1)GET請求
通過URL傳遞參數,URL與參數之間用?隔開,多個參數用&隔開,也是表單的默認提交方式。
GET傳送的數據量較小,這主要是因為受到URL長度的限制。
GET會將數據顯示到URL當中不安全。
基于以上三個特點,GET一般用于直接獲取數據,提高查詢速度。
?請求頭
- Accept-Encoding:gzip,deflate,br----瀏覽器所使用的語言?
- Host:www.baidu.com----遠程主機
- Cookie----cookie
- User-Agent----瀏覽器類型
? 即我們想要請求的信息。
??Socket
Socket(套接字)是計算機網絡編程中用于實現網絡通信的關鍵編程接口(API),它是對底層網絡協議(如 TCP/IP)的封裝,為開發者提供了便捷的網絡通信能力,屏蔽了協議底層的復雜實現細節。
一、Socket 的核心作用
- 標識進程通信:網絡中主機靠 IP 地址唯一標識,而主機內的進程通信依賴 “協議 + 端口”。Socket 結合 IP 地址與端口號(如?
IP 地址:端口號
),唯一標識網絡中的進程,實現不同主機進程間的數據交互。 - 提供通信能力:如同 “發動機”,為程序提供發送和接收數據的網絡通信基礎能力,支持多種網絡協議的應用開發。
二、Socket 的常見類型
- 流式套接字(
SOCK_STREAM
)- 基于?TCP 協議,提供面向連接、可靠的數據傳輸。
- 確保數據無差錯、無重復發送,并按順序接收,內置流量控制(如網頁瀏覽、文件傳輸)。
- 數據包套接字(
SOCK_DGRAM
)- 基于?UDP 協議,提供無連接服務。
- 不保證數據傳輸的可靠性,可能丟失、重復或亂序(如視頻會議、在線游戲)。
- 原始套接字(
SOCK_RAW
)- 允許直接訪問較低層次協議(如 IP、ICMP),用于網絡底層機制調試、新協議開發或網絡監聽。
三、Socket 的工作流程
- 服務端:
- 初始化 Socket 對象,指定協議族(如?
AF_INET
?表示 IPv4)和套接字類型(如?SOCK_STREAM
)。 - 綁定(
bind
)服務端的 IP 地址和端口號。 - 對端口進行監聽(
listen
),等待客戶端連接。 - 接受連接(
accept
),與客戶端建立通信鏈路后,通過讀寫操作(如?read
/write
、recv
/send
)傳輸數據。 - 通信結束后關閉連接。
- 初始化 Socket 對象,指定協議族(如?
- 客戶端:
- 初始化 Socket 對象。
- 連接(
connect
)到服務端的 IP 地址和端口。 - 連接成功后,發送請求數據并接收響應。
- 通信結束后關閉連接。
四、Socket 與協議的關系
- Socket 是工具層:它是網絡通信的編程接口,而非具體協議,支持基于 TCP、UDP 等協議的通信。
- TCP 和 UDP 是實現方式:
- TCP 是面向連接的可靠協議,適合對數據準確性要求高的場景。
- UDP 是無連接的輕量級協議,適合對實時性要求高、容忍少量丟包的場景。
五、通俗比喻輔助理解
將 Socket 比作 “郵局”:
- 每個程序(進程)如同 “城市”,需通過 “郵局(Socket)” 收發信息(數據)。
- 郵局(Socket)負責處理郵件(數據)發送的復雜細節(如分類、路由),程序員無需關心底層網絡協議(如 TCP/IP)的復雜實現,只需通過簡單接口(如發信、收信操作)即可完成通信。
- 不同的郵寄方式(如普通信件、快遞)類似 Socket 支持的不同協議(TCP 可靠、UDP 快速但不保證送達)。
package Servlet.test;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;public class Server {public static void main(String[] args) {// 使用try-with-resources自動關閉ServerSockettry (ServerSocket serverSocket = new ServerSocket(8080)) {System.out.println("服務器啟動,監聽端口: 8080");// 循環監聽多個客戶端連接while (true) {try (Socket socket = serverSocket.accept();InputStream inputStream = socket.getInputStream();BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"))) {System.out.println("客戶端連接成功: " + socket.getInetAddress());// 讀取HTTP請求String line;while ((line = reader.readLine()) != null) {if (line.isEmpty()) { // 空行表示HTTP請求頭結束break;}System.out.println("請求行: " + line);}// 返回簡單的HTTP響應String response = "HTTP/1.1 200 OK\r\n" +"Content-Type: text/html\r\n" +"\r\n" +"<html><body><h1>Hello, World!</h1></body></html>";socket.getOutputStream().write(response.getBytes("UTF-8"));} catch (IOException e) {System.err.println("處理客戶端連接時發生異常: " + e.getMessage());e.printStackTrace();}}} catch (IOException e) {System.err.println("服務器啟動失敗: " + e.getMessage());e.printStackTrace();}}
}
- 創建 ServerSocket 并綁定 8080 端口
- 監聽客戶端連接(accept 方法會阻塞)
- 獲取客戶端輸入流并讀取數據
- 將接收到的字節數據按 UTF-8 編碼轉換為字符串輸出