一、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;从…