關于 java:9. Java 網絡編程

一、Socket 編程

Socket(套接字)是網絡通信的端點,是對 TCP/IP 協議的編程抽象,用于實現兩臺主機間的數據交換。

通俗來說:

  • 可以把 Socket 理解為“電話插口”,插上后客戶端和服務端才能“通話”。

  • Socket = IP 地址 + 端口號(唯一標識一個通信端)

1.1?TCP 通信原理與 Java 實現

1)TCP 特點

特性描述
面向連接通信前需建立連接(三次握手)
可靠傳輸保證數據順序、完整、無丟失
基于字節流數據以流的形式傳輸
雙向通信可同時讀寫

2)通信流程圖(標準 TCP 模型)

客戶端                               服務端| ---------> connect() ----------> || <-------- accept() <------------ || ---------> OutputStream --------|| <--------- InputStream ---------|| ---------> close() ------------>|

3)服務端代碼示例(ServerSocket)

import java.io.*;                       // 導入輸入輸出相關類(BufferedReader、InputStreamReader、PrintWriter 等)
import java.net.*;                      // 導入網絡相關類(ServerSocket、Socket 等)public class TCPServer {               // 定義一個名為 TCPServer 的類public static void main(String[] args) throws IOException {   // 主函數,拋出 IO 異常,避免必須 try-catchServerSocket serverSocket = new ServerSocket(8888);       // 創建一個服務器端的 ServerSocket,監聽 8888 端口System.out.println("服務端啟動,等待連接...");            // 控制臺提示:服務端已啟動,等待客戶端連接Socket socket = serverSocket.accept();                    // 監聽并接受客戶端連接(阻塞式等待,直到客戶端連接為止)System.out.println("客戶端已連接:" + socket.getInetAddress());  // 打印連接上來的客戶端 IP 地址// 讀取客戶端消息BufferedReader reader = new BufferedReader(              // 創建 BufferedReader,從 socket 的輸入流中讀取數據new InputStreamReader(socket.getInputStream())); // 使用 InputStreamReader 把字節流轉為字符流String msg = reader.readLine();                           // 讀取客戶端發送的一行字符串(以換行符為結束標志)System.out.println("收到客戶端消息: " + msg);              // 打印收到的客戶端消息// 給客戶端回應PrintWriter writer = new PrintWriter(                    // 創建 PrintWriter 向客戶端發送數據socket.getOutputStream(), true);                // 獲取 socket 的輸出流,true 表示自動刷新緩沖區writer.println("你好,客戶端,我收到你的消息了。");       // 向客戶端發送一條消息作為回應socket.close();           // 關閉與客戶端通信的 socket(釋放資源)serverSocket.close();     // 關閉服務器的 ServerSocket(停止監聽)}
}

4)客戶端代碼示例(Socket)

import java.io.*;                          // 導入 Java 輸入輸出相關類(如 BufferedReader、PrintWriter 等)
import java.net.*;                         // 導入 Java 網絡通信相關類(如 Socket)public class TCPClient {                   // 定義一個 TCPClient 客戶端類public static void main(String[] args) throws IOException {   // 主函數,拋出 IOException 處理網絡IO異常Socket socket = new Socket("127.0.0.1", 8888);            // 創建 Socket 對象,連接本地 IP 的 8888 端口(連接服務端)// 發送消息PrintWriter writer = new PrintWriter(                     // 創建輸出流 writer,用于向服務端發送數據socket.getOutputStream(), true);                 // 獲取 socket 的輸出流,true 表示自動刷新緩沖區writer.println("你好,我是客戶端");                         // 向服務端發送一行字符串// 讀取回應BufferedReader reader = new BufferedReader(              // 創建輸入流 reader,用于讀取服務端返回的數據new InputStreamReader(socket.getInputStream())); // 將字節輸入流包裝成字符輸入流,再用 BufferedReader 包裝方便讀取String response = reader.readLine();                      // 讀取服務端返回的一行文本(以換行符為結束標志)System.out.println("服務端回應: " + response);             // 打印服務端返回的內容socket.close();                                           // 通信完成后關閉 socket 釋放資源}
}

5)多線程服務端(支持多個客戶端連接)

while (true) {                                               // 無限循環,持續接收客戶端連接Socket socket = serverSocket.accept();                   // 阻塞等待客戶端連接,一旦有客戶端連接就返回 Socketnew Thread(() -> {                                       // 為每個連接創建一個新線程,避免阻塞主線程try {// 讀取客戶端發送的消息BufferedReader reader = new BufferedReader(      // 創建 BufferedReader 包裝輸入流new InputStreamReader(socket.getInputStream())); // 從 socket 獲取輸入流,并轉為字符流String msg = reader.readLine();                  // 讀取客戶端發送的一行消息System.out.println("客戶端消息:" + msg);          // 打印客戶端發送的內容// 向客戶端發送回應PrintWriter writer = new PrintWriter(            // 創建 PrintWriter 用于向客戶端輸出socket.getOutputStream(), true);        // 獲取 socket 的輸出流,true 表示自動 flushwriter.println("服務端回應:" + msg);              // 將收到的消息再返回給客戶端作為回應socket.close();                                  // 關閉連接,釋放資源} catch (IOException e) {e.printStackTrace();                             // 異常處理,打印錯誤信息}}).start();                                               // 啟動新線程,異步處理該客戶端請求
}

1.2?UDP 通信原理與 Java 實現

1)UDP 特點

特性描述
無連接不建立連接,直接發送數據
不可靠可能丟包、亂序
面向報文一次發送 = 一個數據報
快速高效適合實時場景,如直播、游戲

2)通信模型

發送方:DatagramSocket.send(DatagramPacket)
接收方:DatagramSocket.receive(DatagramPacket)

3)UDP 發送端

import java.net.*;                                           // 導入 Java 網絡通信相關類(DatagramSocket、DatagramPacket、InetAddress)public class UDPClient {                                     // 定義一個名為 UDPClient 的類public static void main(String[] args) throws Exception { // 主函數,拋出所有異常DatagramSocket socket = new DatagramSocket();         // 創建 UDP 套接字,用于發送數據(系統自動分配端口)String msg = "Hello UDP Server";                      // 要發送的字符串消息byte[] data = msg.getBytes();                         // 將字符串消息轉換成字節數組(UDP 傳輸的是字節)InetAddress address = InetAddress.getByName("127.0.0.1");  // 獲取本機 IP 地址(目標地址)DatagramPacket packet = new DatagramPacket(           // 創建 UDP 數據報(數據包)data, data.length,                            // 數據內容和數據長度address, 9090);                               // 目標 IP 和目標端口(即服務端的監聽端口)socket.send(packet);                                  // 發送數據報System.out.println("發送完成");                        // 控制臺打印發送成功socket.close();                                       // 關閉 socket,釋放資源}
}

4)UDP 接收端

import java.net.*;                                               // 導入 Java 網絡通信相關類(DatagramSocket、DatagramPacket)public class UDPServer {                                         // 定義一個名為 UDPServer 的類public static void main(String[] args) throws Exception {    // 主函數,拋出所有異常DatagramSocket socket = new DatagramSocket(9090);        // 創建 DatagramSocket 并綁定端口 9090,等待接收客戶端發送的 UDP 數據byte[] buffer = new byte[1024];                          // 創建一個字節數組作為接收緩沖區,最大可接收 1024 字節數據DatagramPacket packet = new DatagramPacket(buffer, buffer.length); // 創建接收用的數據包對象,使用上述緩沖區socket.receive(packet);                                  // 阻塞等待接收客戶端發送的數據包(接收到后將數據寫入 packet 中)String msg = new String(packet.getData(), 0, packet.getLength());  // 將接收到的字節數據轉為字符串(只取有效長度部分)System.out.println("收到客戶端信息:" + msg);               // 打印接收到的客戶端消息內容socket.close();                                          // 關閉 socket,釋放綁定的端口資源}
}

1.3?TCP vs UDP 對比

特性TCPUDP
是否連接需要連接(三次握手)無連接
可靠性可靠,保證順序、無丟包不可靠,可能丟包、亂序
傳輸單位字節流數據報(Datagram)
速度較慢
場景文件傳輸、網頁、數據庫連接視頻流、語音、DNS、廣播等

二、URL、HttpURLConnection 請求發送

Java 的標準庫提供了 java.net.URLjava.net.HttpURLConnection,它們是 HTTP 客戶端的核心類,用于從 Java 應用中發送 GET、POST 等請求,獲取 Web 服務器的響應數據。

2.1?URL 類:用于封裝鏈接地址

URL url = new URL("https://httpbin.org/get");

常用方法:

url.getProtocol();   // 返回 "https"
url.getHost();       // 返回 "httpbin.org"
url.getPath();       // 返回 "/get"
url.openConnection(); // 返回 URLConnection 對象(默認是 HttpURLConnection)

2.2?HttpURLConnection 詳解(發送請求的核心)

獲取連接

HttpURLConnection conn = (HttpURLConnection) url.openConnection();

設置請求方法(GET、POST 等)

conn.setRequestMethod("GET");   // 或 POST

設置連接參數(常用配置)

conn.setConnectTimeout(5000);   // 連接超時時間
conn.setReadTimeout(5000);      // 讀取超時時間
conn.setRequestProperty("User-Agent", "Java-HttpClient"); // 設置請求頭

2.3?GET 請求示例(帶參數)

import java.io.*;                               // 導入輸入輸出相關類(如 BufferedReader、InputStreamReader)
import java.net.*;                              // 導入網絡相關類(如 URL、HttpURLConnection)public class GetExample {                       // 定義一個名為 GetExample 的類public static void main(String[] args) throws Exception {  // 主函數,拋出所有異常以簡化處理String params = "name=test&age=22";                    // 定義 GET 請求的參數字符串(URL 編碼格式)URL url = new URL("https://httpbin.org/get?" + params); // 創建 URL 對象,將參數拼接到 URL 后面(GET 請求通過 URL 傳參)HttpURLConnection conn = (HttpURLConnection) url.openConnection(); // 打開連接,并強制轉換為 HttpURLConnection 對象conn.setRequestMethod("GET");                          // 設置請求方法為 GET(默認其實就是 GET,但寫清楚更規范)BufferedReader reader = new BufferedReader(            // 創建字符緩沖輸入流,用于讀取響應內容new InputStreamReader(conn.getInputStream())); // 從連接中獲取輸入流(即響應體內容)String line;                                           // 定義每行讀取的臨時變量while ((line = reader.readLine()) != null) {           // 逐行讀取響應內容,直到為 null(即讀完)System.out.println(line);                          // 輸出當前行內容到控制臺}reader.close();                                        // 關閉讀取流,釋放資源conn.disconnect();                                     // 斷開 HTTP 連接,釋放網絡資源}
}

2.4?POST 請求示例(表單提交)

import java.io.*;                               // 導入輸入輸出相關類
import java.net.*;                              // 導入網絡相關類public class PostExample {                      // 定義 PostExample 類public static void main(String[] args) throws Exception {  // 主函數,拋出異常URL url = new URL("https://httpbin.org/post");        // 創建目標 URL,指向 httpbin 的 POST 測試接口HttpURLConnection conn = (HttpURLConnection) url.openConnection(); // 打開 HTTP 連接conn.setRequestMethod("POST");                         // 設置請求方法為 POSTconn.setDoOutput(true);                                // 允許向連接寫入請求體(必須設置)conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); // 設置請求頭,表明發送的是表單數據// 寫入請求參數(表單格式)String params = "username=test&password=123456";       // POST 參數,URL 編碼格式字符串OutputStream os = conn.getOutputStream();              // 獲取連接的輸出流os.write(params.getBytes());                            // 將參數轉換成字節流寫入請求體os.flush();                                            // 刷新緩沖區,確保所有數據發送出去os.close();                                            // 關閉輸出流// 讀取服務器響應BufferedReader reader = new BufferedReader(            // 包裝輸入流,方便按行讀取響應內容new InputStreamReader(conn.getInputStream())); // 獲取連接的輸入流(響應體)String line;while ((line = reader.readLine()) != null) {           // 循環讀取每一行System.out.println(line);                           // 打印響應內容到控制臺}reader.close();                                         // 關閉輸入流conn.disconnect();                                      // 斷開 HTTP 連接,釋放資源}
}

2.5?設置請求頭(模擬瀏覽器)

常用請求頭設置:

conn.setRequestProperty("User-Agent", "Mozilla/5.0");
conn.setRequestProperty("Referer", "https://example.com/");
conn.setRequestProperty("Cookie", "session=abc123; token=xyz");

2.6?獲取響應狀態與頭部信息

int code = conn.getResponseCode(); // 200、302、404...
String contentType = conn.getHeaderField("Content-Type");
String setCookie = conn.getHeaderField("Set-Cookie");

2.7?處理 gzip 壓縮響應

某些服務器返回的數據是壓縮的,需解壓讀取:

InputStream in = conn.getInputStream();                      // 從 HttpURLConnection 獲取響應的輸入流(原始數據流)
String encoding = conn.getContentEncoding();                 // 獲取服務器響應頭中的 Content-Encoding 字段(告訴你數據是否壓縮)if ("gzip".equalsIgnoreCase(encoding)) {                     // 判斷響應內容是否使用 gzip 壓縮(忽略大小寫比較)in = new GZIPInputStream(in);                            // 如果是 gzip,則用 GZIPInputStream 對流進行解壓處理
}BufferedReader reader = new BufferedReader(                  // 用 BufferedReader 包裝輸入流,方便逐行讀取字符數據new InputStreamReader(in));                           // InputStreamReader 把字節流轉換成字符流,結合編碼使用

2.8?完整通用封裝(支持 GET/POST)

public static String send(String urlStr, String method, String body, Map<String, String> headers) throws IOException {URL url = new URL(urlStr);                              // 創建 URL 對象,傳入請求地址字符串HttpURLConnection conn = (HttpURLConnection) url.openConnection();  // 打開 HTTP 連接并強制轉換為 HttpURLConnectionconn.setRequestMethod(method);                          // 設置請求方法(GET、POST 等)conn.setConnectTimeout(5000);                           // 設置連接超時時間(5秒)conn.setReadTimeout(5000);                              // 設置讀取響應超時時間(5秒)if (headers != null) {                                  // 如果傳入了請求頭集合for (Map.Entry<String, String> entry : headers.entrySet()) {  // 遍歷所有請求頭conn.setRequestProperty(entry.getKey(), entry.getValue()); // 設置請求頭鍵值對}}if ("POST".equalsIgnoreCase(method) && body != null) { // 如果是 POST 請求且請求體不為空conn.setDoOutput(true);                             // 允許向連接寫入數據(必須)OutputStream os = conn.getOutputStream();           // 獲取輸出流os.write(body.getBytes());                           // 將請求體寫入輸出流(默認字符集)os.close();                                          // 關閉輸出流,完成請求體寫入}BufferedReader reader = new BufferedReader(             // 讀取響應輸入流,方便按行讀取文本new InputStreamReader(conn.getInputStream()));  // 從連接獲取響應流并轉換成字符流StringBuilder sb = new StringBuilder();                  // 創建 StringBuilder 用于拼接響應內容String line;while ((line = reader.readLine()) != null) {             // 循環逐行讀取響應sb.append(line).append("\n");                        // 將每行添加到 StringBuilder 并換行}reader.close();                                          // 關閉讀取流,釋放資源conn.disconnect();                                       // 斷開 HTTP 連接return sb.toString();                                    // 返回拼接好的完整響應字符串
}

2.9?HttpURLConnection 使用建議

使用點建議與說明
多次請求同一主機推薦使用 Apache HttpClient(連接池)
請求體為 JSON設置 Content-Type: application/json 并用 Writer 傳入 JSON 字符串
遇到跳轉(302)默認不自動重定向,需要手動處理
設置代理抓包調試可使用 System.setProperty("http.proxyHost", "127.0.0.1")
異常處理捕獲 IOException, MalformedURLException

2.10?常見問題

問題原因
請求超時網絡不可達、超時時間太短
POST 參數無效setDoOutput(true) 未設置
響應亂碼編碼未設對,建議用 UTF-8
302 重定向失敗HttpURLConnection 默認不跟隨 POST 重定向

2.11 小結

URL url = new URL("...");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET/POST");
conn.setRequestProperty(...);
conn.getOutputStream().write(...); // POST 情況
InputStream in = conn.getInputStream(); // 響應讀取
conn.disconnect();

三、ServerSocket 創建服務端監聽

ServerSocket 是 Java 網絡編程中用于實現 TCP 服務端監聽的類。它能在指定端口持續監聽客戶端連接,一旦有連接到達,就會生成一個 Socket 對象,用于與該客戶端進行后續的通信。

3.1?核心概念:TCP 通信三步走

1. 服務端創建 ServerSocket(綁定端口)
2. 客戶端發起連接 Socket.connect()
3. 服務端 accept() 接收連接,返回 Socket

3.2?ServerSocket 類常用方法

方法作用
new ServerSocket(port)創建監聽端口
accept()阻塞式等待客戶端連接
getInetAddress()獲取連接地址
close()關閉服務端監聽

3.3?最小運行示例(單客戶端)

import java.io.*;                             // 導入輸入輸出相關類(BufferedReader、InputStreamReader、PrintWriter 等)
import java.net.*;                            // 導入網絡相關類(ServerSocket、Socket 等)public class SimpleServer {                   // 定義一個名為 SimpleServer 的類public static void main(String[] args) throws IOException {  // 主函數,拋出 IOException 以處理網絡通信異常ServerSocket serverSocket = new ServerSocket(8888);      // 創建服務器監聽套接字,綁定端口 8888System.out.println("服務端啟動,監聽端口 8888");          // 控制臺提示服務已啟動Socket socket = serverSocket.accept();                   // 阻塞等待客戶端連接(連接成功返回 Socket 對象)System.out.println("客戶端已連接:" + socket.getInetAddress()); // 打印連接的客戶端 IP 地址BufferedReader reader = new BufferedReader(              // 創建字符緩沖輸入流,讀取客戶端發來的數據new InputStreamReader(socket.getInputStream())); // 獲取輸入流并轉為字符流String msg = reader.readLine();                          // 讀取一行數據(以換行符為結束標志)System.out.println("收到消息:" + msg);                   // 打印客戶端消息內容PrintWriter writer = new PrintWriter(                    // 創建輸出流,用于發送數據給客戶端socket.getOutputStream(), true);                // 第二個參數 true 表示自動刷新(無需手動 flush)writer.println("服務端收到你的消息啦");                   // 向客戶端發送響應socket.close();                                          // 通信結束后關閉客戶端連接serverSocket.close();                                    // 關閉服務端監聽端口,釋放資源}
}

3.4?配套客戶端(測試使用)

import java.io.*;                               // 導入輸入輸出相關類(PrintWriter、BufferedReader 等)
import java.net.*;                              // 導入網絡通信相關類(Socket)public class SimpleClient {                     // 定義客戶端類 SimpleClientpublic static void main(String[] args) throws IOException {  // 主函數,拋出 IOException 異常(用于網絡通信處理)Socket socket = new Socket("127.0.0.1", 8888);           // 創建一個 Socket,連接本機 IP 的 8888 端口(服務端)PrintWriter writer = new PrintWriter(                    // 創建字符輸出流,向服務端發送數據socket.getOutputStream(), true);                // 獲取 socket 的輸出流,true 表示自動刷新緩沖區writer.println("你好,我是客戶端!");                      // 發送一行文本數據給服務端BufferedReader reader = new BufferedReader(              // 創建字符輸入流,用于讀取服務端返回的數據new InputStreamReader(socket.getInputStream())); // 獲取 socket 的輸入流,并轉為字符流String response = reader.readLine();                     // 讀取服務端返回的一行字符串System.out.println("服務端回應:" + response);            // 打印服務端返回的消息socket.close();                                          // 關閉 socket 連接,釋放資源}
}

3.5?支持多客戶端連接(多線程服務端)

一個服務端接收多個客戶端,必須使用 多線程 處理每個連接。

import java.io.*;                            // 導入輸入輸出相關類(BufferedReader、PrintWriter 等)
import java.net.*;                           // 導入網絡通信相關類(ServerSocket、Socket 等)public class MultiClientServer {             // 主類:服務端public static void main(String[] args) throws IOException {   // 主方法,拋出 IOException(用于處理網絡通信錯誤)ServerSocket serverSocket = new ServerSocket(8888);       // 創建服務器監聽套接字,監聽端口 8888System.out.println("服務端啟動,監聽端口 8888");             // 提示服務端已啟動while (true) {                                            // 無限循環,持續接受客戶端連接Socket client = serverSocket.accept();                // 阻塞等待客戶端連接,連接成功返回 Socketnew Thread(new ClientHandler(client)).start();        // 每一個客戶端連接都啟動一個新線程處理(多線程處理并發)}}
}class ClientHandler implements Runnable {     // 定義客戶端處理器類,實現 Runnable 接口(可被線程執行)private Socket socket;                    // 每個對象持有一個客戶端連接 Socketpublic ClientHandler(Socket socket) {     // 構造函數,接收客戶端 Socket 并賦值this.socket = socket;}public void run() {                       // run 方法是線程執行的主體try {System.out.println("連接客戶端:" + socket.getInetAddress());  // 打印客戶端 IP 地址BufferedReader reader = new BufferedReader(                  // 獲取輸入流,用于讀取客戶端發來的數據new InputStreamReader(socket.getInputStream()));String msg = reader.readLine();                              // 讀取客戶端發來的消息(阻塞直到有數據)System.out.println("收到消息:" + msg);                       // 打印收到的消息PrintWriter writer = new PrintWriter(                        // 獲取輸出流,用于回應客戶端socket.getOutputStream(), true);writer.println("收到你的消息啦,線程ID:" + Thread.currentThread().getId());  // 回復內容中加入當前線程 IDsocket.close();                                              // 通信完成后關閉 Socket 連接} catch (IOException e) {e.printStackTrace();                                         // 異常打印(例如連接斷開時)}}
}

3.6?ServerSocket 構造函數詳解

new ServerSocket(port);                         // 默認本地地址監聽
new ServerSocket(port, backlog);               // 指定連接隊列長度
new ServerSocket(port, backlog, InetAddress);  // 指定本地 IP(如多網卡)
  • port: 要監聽的端口(0~65535,推薦 >= 1024)

  • backlog: 同時等待連接的最大數量(默認50)

  • InetAddress: 指定綁定地址(如綁定公網IP)

3.7?服務端監聽常見應用場景

場景實現思路
HTTP Web 服務模擬客戶端發送 HTTP 請求報文,服務端解析并構造響應
聊天室服務端多客戶端連接,廣播消息給所有人
命令控制接口服務端接受控制指令,執行并返回結果
文件傳輸服務客戶端上傳/下載文件

3.8?服務端監聽與請求結構(HTTP 示例)

客戶端請求:
GET / HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.58.0服務端可讀取:
GET / HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.58.0

服務端可返回:

HTTP/1.1 200 OK
Content-Type: text/plainHello from Server

3.9?常見錯誤排查

錯誤說明
BindException: Address already in use端口被占用
ConnectException: Connection refused客戶端找不到服務端
SocketException: Socket closed連接未正常關閉
EOFException客戶端提前關閉連接

3.10 小結

ServerSocket server = new ServerSocket(8888);while (true) {Socket client = server.accept();  // 阻塞接收連接new Thread(() -> {InputStream in = client.getInputStream();OutputStream out = client.getOutputStream();// 處理請求 + 返回響應}).start();
}

四、底層協議包抓包分析

抓包(Packet Sniffing)是逆向分析、協議還原、爬蟲繞過、參數定位的核心技能之一。

抓包可以:

  • 查看真實請求頭/參數/內容

  • 分析加密數據的位置和結構

  • 識別 Cookie / Token / Headers

  • 還原移動 APP / 小程序 / 加密 JS 的真實通信行為

4.1?抓包工具對比

工具特點適用場景
Wireshark抓底層 TCP/IP 包,包含所有協議TCP/UDP/SSL 分析,低層協議
mitmproxy抓取 HTTPS 應用層請求,支持中間人證書抓取 HTTP/HTTPS 請求、反爬分析
FiddlerWindows 專用 GUI 抓包工具類似 mitmproxy,圖形界面
Charles跨平臺 GUI 抓包工具抓取手機/瀏覽器通信

mitmproxy它是 跨平臺 + 支持腳本分析 + 免費 + 支持 CLI/GUI 的強力工具。

4.2?mitmproxy 抓包工具安裝與使用

1)安裝(需 Python 環境)

pip install mitmproxy

2)啟動 Web GUI 界面(推薦)

mitmweb

默認監聽 127.0.0.1:8080,并打開 Web UI 界面(http://127.0.0.1:8081)

4.3?mitmproxy 實現“中間人”原理

[Java程序/瀏覽器] →→ mitmproxy(偽裝服務器) →→ 目標網站mitmproxy 攔截請求并生成“偽造證書”

HTTPS 抓包必須讓系統或瀏覽器信任 mitmproxy 的證書

4.4?mitmproxy 證書安裝(用于抓取 HTTPS)

手機抓包(Android):

  1. 手機設置代理為:WiFi → 高級 → HTTP代理 → 手動(填入 電腦IP:8080

  2. 手機瀏覽器訪問:http://mitm.it

  3. 下載 Android 根證書并安裝(系統或用戶證書)

電腦抓包(Java 應用):

Java 默認不信任 mitmproxy 的證書。可用兩種方式解決:

方法 1:禁用 SSL 驗證(開發調試可用)

import javax.net.ssl.*;  // 引入 Java 提供的 SSL 通信相關的類和接口public class SSLBypass {static {try {// 創建一個“信任所有證書”的 TrustManager 數組TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {}  // 不檢查客戶端證書public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {}  // 不檢查服務端證書public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; }              // 返回 null 表示接受所有}};// 初始化一個 SSLContext,使用上面的“信任所有證書”的 TrustManagerSSLContext sc = SSLContext.getInstance("SSL");sc.init(null, trustAllCerts, new java.security.SecureRandom());// 將默認的 SSLSocketFactory 替換成上面這個“信任所有證書”的版本HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());// 創建并設置一個“信任所有主機名”的 HostnameVerifier(域名不匹配也通過)HostnameVerifier hv = (hostname, session) -> true;HttpsURLConnection.setDefaultHostnameVerifier(hv);} catch (Exception e) {e.printStackTrace();  // 捕獲并打印異常(一般不會觸發)}}
}

方法 2:導入 mitmproxy 證書到 Java 信任庫

# 導出 mitmproxy 證書(例如下載到 mitmproxy-ca-cert.pem)
openssl x509 -inform PEM -in mitmproxy-ca-cert.pem -out mitmproxy.crt# 導入到 JDK 信任庫
keytool -import -trustcacerts -alias mitmproxy -file mitmproxy.crt -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit

4.5?Java 設置代理(讓請求流經 mitmproxy)

設置系統代理(全局)

System.setProperty("http.proxyHost", "127.0.0.1");
System.setProperty("http.proxyPort", "8080");
System.setProperty("https.proxyHost", "127.0.0.1");
System.setProperty("https.proxyPort", "8080");

設置單次代理(推薦):

Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 8080));
HttpURLConnection conn = (HttpURLConnection) new URL("https://httpbin.org/get").openConnection(proxy);

4.6?實際抓包分析內容

一旦 Java 程序流量經過 mitmproxy,將在 mitmweb 中看到:

抓到的內容說明
請求方法與地址GET /api/login
請求頭(Headers)User-Agent、Cookie、Referer
請求體(POST body)登錄用戶名密碼、form 表單內容
響應內容HTML 頁面、JSON 數據、狀態碼
響應 Set-Cookie登錄狀態維持的重要標志

4.7?使用 mitmproxy 腳本進一步分析/修改流量

創建 modify_request.py

def request(flow):if flow.request.pretty_url.startswith("https://target.com/api"):print("請求參數:", flow.request.text)flow.request.headers["X-Intercepted"] = "True"

運行:

mitmdump -s modify_request.py

4.8?示意流程圖

[ Java應用 ]↓   代理設置 (127.0.0.1:8080)
[ mitmproxy ] ← 攔截 HTTPS 流量,解密數據↓
[ 真實服務器 ]

4.9 小結

步驟工具/方式
抓取網頁或 APP 的請求mitmproxy、Fiddler、Charles
設置 Java 代理Proxy 類 或 System.setProperty
證書處理(HTTPS)導入證書 / 忽略驗證
分析參數結構Headers、Form、Cookie、URL 參數
還原請求Java 模擬 GET/POST,構造對應參數

五、模擬登錄、表單提交、cookie 操作

真實網站很多頁面必須登錄后才能訪問,比如:

  • 用戶中心、購物車、訂單頁

  • 發帖、點贊、評論等操作

  • 管理后臺數據采集

所以需要學會:

? 模擬登錄 ? 模擬表單提交 ? 維持 Cookie ? 訪問受限頁面

真實網站登錄流程簡化圖

1. GET 登錄頁面(拿 Cookie + token)
2. POST 表單提交(用戶名+密碼+token)
3. 響應中返回 Set-Cookie(登錄成功)
4. 后續請求需帶 Cookie

5.1?實戰步驟詳解(Java 實現)

Step 1:請求登錄頁(獲取初始 Cookie)

URL loginPageUrl = new URL("https://example.com/login");         // 創建一個 URL 對象,指向登錄頁面地址
HttpURLConnection conn = (HttpURLConnection) loginPageUrl.openConnection();  // 打開連接,強制轉換為 HttpURLConnection
conn.setRequestProperty("User-Agent", "Mozilla/5.0");            // 設置請求頭中的 User-Agent,模擬瀏覽器訪問String setCookie = conn.getHeaderField("Set-Cookie");            // 獲取響應頭中名為 Set-Cookie 的字段(服務器下發的 Cookie)
System.out.println("初始 Cookie:" + setCookie);                 // 打印獲取到的 Cookie 字符串

Step 2:構造 POST 請求,提交表單(模擬用戶登錄)

URL loginAction = new URL("https://example.com/doLogin");           // 構造登錄請求的 URL(POST 接口地址)
HttpURLConnection loginConn = (HttpURLConnection) loginAction.openConnection();  // 打開連接并強轉為 HttpURLConnection
loginConn.setRequestMethod("POST");                                 // 設置請求方法為 POST
loginConn.setDoOutput(true);                                        // 允許向請求體寫數據(POST 必須)// 偽裝成瀏覽器請求,設置常見請求頭
loginConn.setRequestProperty("User-Agent", "Mozilla/5.0");          // 模擬瀏覽器的 User-Agent
loginConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");  // 設置請求體內容類型為表單格式
loginConn.setRequestProperty("Cookie", setCookie);                  // 附帶之前獲取的 Cookie(比如登錄頁返回的 Cookie)// 構造 POST 表單內容,注意參數需要 URL 編碼
String body = "username=admin&password=123456";                     // 登錄參數(賬號密碼)
OutputStream os = loginConn.getOutputStream();                      // 獲取連接的輸出流
os.write(body.getBytes());                                          // 將表單參數寫入請求體(默認編碼)
os.flush();                                                        // 刷新緩沖區,確保數據發送出去
os.close();                                                        // 關閉流// 讀取服務器返回的新 Cookie(登錄成功后通常會更新會話 Cookie)
String loginCookie = loginConn.getHeaderField("Set-Cookie");       // 獲取響應頭中新的 Set-Cookie
System.out.println("登錄后的 Cookie:" + loginCookie);              // 打印登錄后返回的 Cookie 字符串

Step 3:訪問登錄后的頁面(帶上 Cookie)

URL userCenter = new URL("https://example.com/user/home");           // 構造用戶主頁 URL 地址
HttpURLConnection userConn = (HttpURLConnection) userCenter.openConnection();  // 打開連接并轉為 HttpURLConnection
userConn.setRequestProperty("User-Agent", "Mozilla/5.0");            // 設置 User-Agent 模擬瀏覽器請求
userConn.setRequestProperty("Cookie", loginCookie);                  // 攜帶登錄時服務器返回的 Cookie,保持登錄狀態BufferedReader reader = new BufferedReader(                           // 創建緩沖字符輸入流,讀取響應內容new InputStreamReader(userConn.getInputStream()));            // 獲取連接的輸入流,讀取響應體
String line;
while ((line = reader.readLine()) != null) {                         // 按行讀取響應內容,直到讀完System.out.println(line);                                         // 打印每一行到控制臺
}
reader.close();                                                      // 關閉輸入流,釋放資源

5.2?Cookie 詳解(登錄狀態維持)

  • 服務端登錄成功后返回 Set-Cookie

  • 后續所有請求都需要帶這個 Cookie 否則會被認為是“未登錄”

  • Java 中可以:

    • 手動設置 Cookie

    • 或使用 CookieManager 自動管理多個 Cookie

5.3?完整封裝工具類

public class HttpUtils {                                           // 定義 HttpUtils 工具類,封裝 HTTP 登錄和頁面請求功能public static String loginAndGetCookie(String loginUrl, String params, String initCookie) throws IOException {URL url = new URL(loginUrl);                               // 創建登錄接口的 URL 對象HttpURLConnection conn = (HttpURLConnection) url.openConnection(); // 打開連接,強制轉換為 HttpURLConnectionconn.setRequestMethod("POST");                             // 設置請求方法為 POST,表示發送數據conn.setDoOutput(true);                                    // 允許向連接輸出數據(寫請求體)conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); // 設置請求體格式為表單格式conn.setRequestProperty("User-Agent", "Mozilla/5.0");     // 設置請求頭 User-Agent,模擬瀏覽器訪問if (initCookie != null)                                    // 如果有傳入初始 Cookie,則設置 Cookie 請求頭conn.setRequestProperty("Cookie", initCookie);OutputStream os = conn.getOutputStream();                  // 獲取連接的輸出流,用于寫入請求體數據os.write(params.getBytes());                               // 將登錄參數(字符串)寫入請求體,默認編碼os.close();                                                // 關閉輸出流,結束請求體寫入return conn.getHeaderField("Set-Cookie");                  // 從響應頭獲取服務器返回的 Set-Cookie,返回給調用方}public static String getPage(String urlStr, String cookie) throws IOException {URL url = new URL(urlStr);                                 // 創建要訪問頁面的 URL 對象HttpURLConnection conn = (HttpURLConnection) url.openConnection(); // 打開連接,強轉為 HttpURLConnectionconn.setRequestProperty("User-Agent", "Mozilla/5.0");     // 設置請求頭 User-Agent,模擬瀏覽器訪問if (cookie != null)                                        // 如果傳入 Cookie,設置 Cookie 請求頭,帶上登錄態conn.setRequestProperty("Cookie", cookie);BufferedReader reader = new BufferedReader(                // 創建緩沖字符輸入流,方便逐行讀取響應體new InputStreamReader(conn.getInputStream()));     // 獲取響應輸入流并轉換成字符流StringBuilder sb = new StringBuilder();                    // 創建 StringBuilder 用于拼接讀取的頁面內容String line;                                               // 定義變量存儲每行讀取的字符串while ((line = reader.readLine()) != null)                 // 循環讀取響應的每一行,直到結束sb.append(line).append("\n");                          // 將每行內容添加到 StringBuilder 并換行return sb.toString();                                      // 返回拼接完成的完整頁面內容字符串}
}

5.4?如何分析真實網站的登錄行為?

借助瀏覽器/mitmproxy 抓包:

Chrome:

  • 打開 DevTools → Network → login 接口

  • 查看:

    • 請求方法(POST/GET)

    • 表單字段名(username/password/captcha/token)

    • Headers(Referer/User-Agent/Cookie)

    • 響應中的 Set-Cookie

mitmproxy:

抓 HTTPS 全流量,分析:

  • 真實請求體參數

  • 登錄是否加密(sign/token)

  • Cookie 是登錄成功的關鍵

5.5?應對驗證碼、token、重定向、加密參數

問題處理方法
登錄有驗證碼用 Python 識別圖形驗證碼 / OCR + 人工輔助
表單中有 CSRF token抓登錄頁 HTML,提取隱藏字段 name="csrf_token"
登錄成功后 302跟蹤 Location 頭,發起二次請求
有 JS 加密參數需逆向 JS 算法,還原加密邏輯(推薦 mitmproxy + 調試)

5.6 小結

模擬登錄 = 抓登錄請求結構 + 提交表單字段 + 解析返回Cookie + 維持 Cookie 請求受限頁

如果要做自動化平臺、信息采集或爬蟲登錄:

  • 封裝好 Cookie + 登錄邏輯類

  • 實現 Cookie 自動保存(本地文件/數據庫)

  • 搭配驗證碼識別或逆向 JS 還原參數

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/87073.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/87073.shtml
英文地址,請注明出處:http://en.pswp.cn/web/87073.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

主流零信任安全產品深度介紹

騰訊 iOA 零信任安全管理系統 功能&#xff1a;提供零信任接入、終端安全、數據防泄密等十余種功能模塊。可實現基于身份的動態訪問控制、終端安全一體化防護、數據防泄密體系等。核心優勢&#xff1a;基于騰訊內部千萬級終端實踐打磨&#xff0c;沉淀豐富場景方案&#xff0c…

LabVIEW裝配車體撓度無線測量

針對軌道交通車輛裝配過程中車體撓度測量需求&#xff0c;基于LabVIEW開發無線快速測量系統&#xff0c;采用品牌硬件構建高精度數據采集與傳輸架構。系統通過 ZigBee 無線傳輸技術、高精度模數轉換模塊及激光位移傳感器&#xff0c;實現裝配車體撓度的實時、自動、非接觸測量&…

java微服務-linux單機CPU接近100%優化

你這個場景&#xff1a; 4核16G 機器 同時運行了 8個 Spring Boot 微服務&#xff0c;每個 JAR 文件 100多 MB 導致 CPU 接近100% 確實是一個常見但資源緊繃的部署情境。下面是分層的優化建議&#xff0c;包括 JVM、系統、服務架構等多個方面&#xff0c;幫助你 降 CPU、穩…

MySQL表的約束和基本查詢

一.表的約束 1.1空屬性 當我們填寫問卷的時候,經常會有不允許為空的問題,比如電話號,姓名等等.而mysql上我們可以在創建表的時候,如果想要某一列不允許為空,可以加上not null來加以限制: mysql> create table myclass( -> class_name varchar(20) not null, -> cla…

VBA代碼解決方案第二十六講:如何新建EXCEL工作簿文件

《VBA代碼解決方案》(版權10028096)這套教程是我最早推出的教程&#xff0c;目前已經是第三版修訂了。這套教程定位于入門后的提高&#xff0c;在學習這套教程過程中&#xff0c;側重點是要理解及掌握我的“積木編程”思想。要靈活運用教程中的實例像搭積木一樣把自己喜歡的代碼…

【unity游戲開發——網絡】套接字Socket的重要API

注意&#xff1a;考慮到熱更新的內容比較多&#xff0c;我將熱更新的內容分開&#xff0c;并全部整合放在【unity游戲開發——網絡】專欄里&#xff0c;感興趣的小伙伴可以前往逐一查看學習。 文章目錄 1、Socket套接字的作用2、Socket類型與創建3、核心屬性速查表4、關鍵方法指…

計算機網絡(二)應用層HTTP協議

目錄 1、HTTP概念 ?編輯2、工作流程?? 3、HTTP vs HTTPS?? 4、HTTP請求特征總結? 5、持久性和非持久性連接 非持久連接&#xff08;HTTP/1.0&#xff09;?? ??持久連接&#xff08;HTTP/1.1&#xff09;?? 1、HTTP概念 HTTP&#xff08;HyperText Transfer …

c# IO密集型與CPU密集型任務詳解,以及在異步編程中的使用示例

文章目錄 IO密集型與CPU密集型任務詳解&#xff08;C#示例&#xff09;一、基本概念1. IO密集型任務2. CPU密集型任務 二、C#示例1. IO密集型示例1.1 文件操作異步示例1.2 網絡請求異步示例1.3 數據庫操作異步示例 2. CPU密集型示例2.1 基本CPU密集型異步處理2.2 并行處理CPU密…

用lines_gauss的width屬性提取缺陷

自己做了一個圖&#xff0c;這個圖放在資源里了 結果圖是這樣&#xff08;這里只結算了窄區&#xff09; 代碼和備注如下 read_image (Image11, C:/Users/Administrator/Desktop/分享/15/11.png) rgb1_to_gray (Image11, GrayImage) invert_image (GrayImage, ImageInvert) thr…

從0到100:房產中介小程序開發筆記(中)

背景調研 為中介帶來諸多優勢&#xff0c;能借助它打造專屬小程序&#xff0c;方便及時更新核實租賃信息&#xff0c;確保信息準確無誤&#xff0c;像房屋的大致地址、租金數額、租賃條件、房源優缺點等關鍵信息都能清晰呈現。還可上傳房屋拍攝照片&#xff0c;這樣用戶能提前…

【AI 時代的網絡爬蟲新形態與防護思路研究】

網絡爬蟲原理與攻擊防護的深度研究報告 網絡爬蟲技術已進入AI驅動的4.0時代&#xff0c;全球自動化請求流量占比突破51%&#xff0c;傳統防御手段在面對高度仿真的AI爬蟲時已顯疲態。基于2025年最新數據&#xff0c;深入剖析網絡爬蟲的基本原理、工作流程、分類與攻擊方式&…

低代碼平臺架構設計與關鍵組件

低代碼平臺的架構設計是其核心能力的關鍵支撐&#xff0c;需要平衡可視化開發的便捷性、生成應用的健壯性與性能、可擴展性以及企業級需求&#xff08;如安全、多租戶、集成&#xff09;。以下是一個典型的企業級低代碼平臺架構概覽及其關鍵組件&#xff1a; https://example.…

電商 ERP 系統集成接口指南

電商 ERP 系統的高效運行依賴于與多個業務系統的無縫對接&#xff0c;需要集成的核心接口包括&#xff1a;商品管理、訂單處理、庫存同步、物流配送、客戶管理、財務結算等。這些接口是實現數據互通、業務協同的關鍵橋梁。 一、電商 ERP 系統集成所需接口類型 &#xff08;一…

Python實現對WPS協作群進行群消息自動推送

前言 本文是該專欄的第59篇,后面會持續分享python的各種干貨知識,值得關注。 相信有些同學在工作或者項目中,都會使用到“WPS協作”作為辦公聊天軟件。如果說,有些項目的監控預警正好需要你同步到WPS協作群,這個時候需要怎么去做呢? 而本文,筆者將基于WPS協作,通過Py…

js嚴格模式和非嚴格模式

好的&#xff0c;這是一個非常基礎且重要的概念。我們來詳細解析一下 JavaScript 中的嚴格模式&#xff08;Strict Mode&#xff09;和非嚴格模式&#xff08;Sloppy Mode&#xff09;。 可以把它想象成參加一場考試&#xff1a; 非嚴格模式&#xff1a;就像是開卷、不計時的…

板凳-------Mysql cookbook學習 (十一--------1)

第11章&#xff1a;生成和使用序列 11.0 引言 11.1 創建一個序列列并生成序列值 CREATE TABLE insect ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY (id)&#xff0c;name VARCHAR(30) NOT NULL,date DATE NOT NULL,origin VARCHAR(30) NOT NULL); 字段說明 ?id…

Vue3 中 Excel 導出的性能優化與實戰指南

文章目錄 Vue3 中 Excel 導出的性能優化與實戰指南引言&#xff1a;為什么你的導出功能會卡死瀏覽器&#xff1f;一、前端導出方案深度剖析1.1 xlsx (SheetJS) - 輕量級冠軍1.2 exceljs - 功能強大的重量級選手 二、后端導出方案&#xff1a;大數據處理的救星2.1 為什么大數據需…

安卓RecyclerView實現3D滑動輪播效果全流程實戰

安卓RecyclerView實現3D滑動輪播效果全流程實戰 1. 前言 作為一名學習安卓的人,在接觸之前和之后兩種完全不同的想法: 好看和怎么實現 當初接觸到RecyclerView就覺得這個控件就可以把關于列表的所有UI實現,即便不能,也是功能十分強大 放在現在依然是應用最廣的滑動列表控…

電機控制——電機位置傳感器零位標定

在有感FOC算法中電機位置是一個重要的輸入&#xff0c;電機位置傳感器的作用就是測量電機的旋轉角度&#xff0c;通常是輸出sin(Theta)和cos(Theta)兩路模擬信號&#xff0c;根據這兩路模擬信號測得電機旋轉絕對角度。注意傳感器測量的是機械角度&#xff0c;不是電角度。 關于…

生物化學(實驗流程) PCR聚合酶鏈式反應: DNA 凝膠電泳實驗原理 實驗流程方法 實操建議筆記

凝膠電泳是分子生物學中最常用的技術之一&#xff0c;廣泛用于 DNA 片段的可視化、分離與識別。在獲取DNA 凝膠電泳相關設備&#xff08;電泳設備 & DNA樣品染料 & 凝膠 & 染料&#xff09;之后&#xff0c;可以考慮進行電泳操作。 整體電泳操作流程&#xff08;從…