不同协议框架类接口


除了 HTTP、Thrift 和 Pigeon,常见的接口类型/协议/框架非常丰富,它们在设计目标、性能、使用场景和生态系统上各有侧重。

一、 网络协议类接口 (通常是标准协议)

  1. HTTP/HTTPS:

    • 应用层协议,基于请求-响应模型。
    • RESTful API: 目前最流行的 Web API 设计风格,利用 HTTP 方法 (GET, POST, PUT, DELETE 等) 和状态码。
    • SOAP: 基于 XML 的协议,通常通过 HTTP(S) 传输,定义严格(WSDL),更重量级,常用于企业级集成。
    • GraphQL: 一种用于 API 的查询语言,允许客户端精确指定所需数据,通常运行在 HTTP 上。
  2. WebSocket:

    • 在单个 TCP 连接上提供全双工通信通道的协议。非常适合需要实时双向通信的应用,如聊天、实时数据推送、在线游戏。
  3. TCP/UDP:

    • 传输层协议。许多自定义协议直接构建在 TCP 或 UDP 之上,以满足特定性能或功能需求(例如,游戏协议、物联网设备协议、高性能金融交易协议)。
  4. FTP/SFTP/FTPS:

    • 专门用于文件传输的协议。SFTP (SSH File Transfer Protocol) 和 FTPS (FTP over SSL/TLS) 提供了安全性。
  5. gRPC:

    • 虽然是一个框架,但其核心协议是标准的。基于 HTTP/2,使用 Protocol Buffers 作为默认的接口定义语言和序列化机制。提供高性能、跨语言支持、强类型接口定义,支持多种调用模式(Unary, Server Streaming, Client Streaming, Bidirectional Streaming)。

二、 RPC 框架类接口 (通常提供协议、序列化、服务发现等全套解决方案)

  1. gRPC: (再次列出,因其既是协议也是框架) 由 Google 开发,非常流行。
  2. Apache Dubbo: 高性能 Java RPC 框架,在国内尤其流行,提供丰富的服务治理能力。
  3. Apache Thrift: 由 Facebook 开发,支持多种语言,强调高性能和跨语言。
  4. Pigeon: 美团开源的基于 Netty 的 RPC 框架,主要服务于 Java 生态。
  5. JSON-RPC / XML-RPC:
    • 非常轻量级的 RPC 协议,分别使用 JSON 或 XML 作为数据格式。结构简单,易于实现,常用于 Web 和简单服务间调用。
  6. Apache Avro:
    • 强调 Schema 演化(Schema Evolution)的数据序列化系统,常用于 Hadoop 生态,但也支持 RPC。其 RPC 能力通常与 Hadoop RPC 关联。
  7. CORBA:
    • 较老的标准,旨在实现不同语言、不同平台对象间的互操作,比较复杂和重量级,现在新项目用得较少,但一些遗留系统可能还在使用。

三、 消息队列接口/协议

  1. AMQP:
    • 高级消息队列协议,RabbitMQ 是其最著名的实现。定义了消息的格式、路由规则(Exchange, Binding, Queue),提供可靠传输、事务、确认等机制。
  2. Kafka Protocol:
    • Apache Kafka 使用自定义的二进制协议进行客户端与 Broker 之间的通信。虽然底层是 TCP,但其消息格式、API 是 Kafka 特有的。
  3. MQTT:
    • 轻量级的发布/订阅消息传输协议,专为低带宽、高延迟或不稳定网络环境(如物联网)设计。
  4. STOMP:
    • 简单的文本导向消息协议,为消息中间件提供可互操作的连接格式。

四、 数据库访问接口

  1. JDBC: Java 数据库连接标准 API。
  2. ODBC: 开放数据库连接,更通用的数据库访问标准。
  3. 特定数据库协议: 如 MySQL Client/Server Protocol, PostgreSQL Frontend/Backend Protocol, Redis RESP (REdis Serialization Protocol) 等。

五、 进程间通信 (IPC) 接口

  1. Unix Domain Sockets:
    • 在同一台主机上的进程间进行高性能通信的接口,比 TCP/IP 环回接口更快、开销更低。
  2. Named Pipes / FIFOs:
    • 另一种在同一主机上进程间通信的机制。
  3. Shared Memory:
    • 多个进程访问同一块内存区域,是最快的 IPC 方式,但需要处理同步问题。

六、 服务网格接口

  1. Envoy xDS API:
    • 在服务网格(如 Istio, Consul Connect)中,控制平面通过 xDS (如 CDS, EDS, LDS, RDS) 协议动态配置数据平面代理(如 Envoy)。
  2. gRPC / HTTP/2:
    • 服务网格内的服务间通信通常透明地使用 gRPC 或 HTTP/2 协议。

总结关键点

  • 按层次分: 有底层传输协议 (TCP/UDP)、应用层协议 (HTTP, MQTT, AMQP)、RPC 框架 (gRPC, Thrift, Dubbo)、API 风格 (REST, GraphQL)。
  • 按模式分: 请求-响应 (HTTP REST, gRPC Unary, RPC)、发布-订阅 (MQTT, Kafka)、流式 (gRPC Streaming, WebSocket)、文件传输 (FTP/SFTP)。
  • 按性能/特性: 追求极致性能 (自定义 TCP/UDP 协议, gRPC, Thrift)、强调简单易用 (REST, JSON-RPC)、适合 实时双向 (WebSocket)、适合物联网 (MQTT)、适合大数据流 (Kafka)。
  • 按生态/场景: Web 开发 (HTTP REST, GraphQL)、微服务 (gRPC, Dubbo, Thrift, HTTP REST)、消息驱动 (Kafka, RabbitMQ/AMQP, MQTT)、物联网 (MQTT)、遗留系统 (CORBA, SOAP)、文件传输 (FTP/SFTP)、数据库访问 (JDBC/ODBC)、进程通信 (Sockets, Pipes, Shared Mem)。

选择哪种接口类型取决于具体的应用场景、性能要求、开发团队熟悉度、语言支持、现有基础设施和生态系统集成需求。HTTP REST 和 gRPC 是目前构建现代分布式系统(尤其是微服务)最主流的选择。

GraphQL 的主要用途

GraphQL 是一种用于 API 的查询语言和运行时,核心用途包括:

  1. 精确数据获取:客户端指定所需字段,避免 RESTful API 的“过度获取”或“欠获取”。
  2. 单一端点请求:所有操作(查询、修改、订阅)通过单个端点(如 /graphql)处理。
  3. 强类型系统:基于 Schema 定义数据类型,提供自动验证和文档。
  4. 聚合多源数据:统一多个后端服务(数据库、微服务、第三方 API)的数据。
  5. 实时数据:通过订阅(Subscriptions)支持实时更新。

查询的类型和方法

GraphQL 操作分为三类:

1. 查询(Query)

用于 读取数据(类似 RESTful 的 GET)。
示例:获取书籍及其作者信息

query GetBook {
  book(id: "101") {
    title
    author {
      name
      country
    }
  }
}

响应

{
  "data": {
    "book": {
      "title": "The Great Gatsby",
      "author": {
        "name": "F. Scott Fitzgerald",
        "country": "USA"
      }
    }
  }
}

2. 变更(Mutation)

用于 修改数据(类似 RESTful 的 POST/PUT/DELETE)。
示例:添加新书

mutation AddBook {
  addBook(title: "1984", authorId: "202") {
    id  # 返回新书的ID
    title
  }
}

响应

{
  "data": {
    "addBook": {
      "id": "103",
      "title": "1984"
    }
  }
}

3. 订阅(Subscription)

用于 实时数据推送(基于 WebSocket)。
示例:监听新评论

subscription OnNewComment {
  newComment(bookId: "101") {
    id
    text
    user
  }
}

推送响应(当有新评论时):

{
  "data": {
    "newComment": {
      "id": "9001",
      "text": "A masterpiece!",
      "user": "Alice"
    }
  }
}

高级查询方法

1. 参数(Arguments)

字段传递参数过滤数据:

query {
  books(genre: "Sci-Fi", limit: 5) {
    title
  }
}

2. 别名(Aliases)

避免字段名冲突:

query {
  firstBook: book(id: "101") { title }
  secondBook: book(id: "102") { title }
}

3. 片段(Fragments)

复用字段集合:

fragment bookFields on Book {
  title
  author { name }
}
query {
  book1: book(id: "101") { ...bookFields }
  book2: book(id: "102") { ...bookFields }
}

4. 变量(Variables)

动态传入参数:

query GetBook($id: ID!, $withAuthor: Boolean = true) {
  book(id: $id) {
    title
    author @include(if: $withAuthor) { name }
  }
}

变量输入

{ "id": "101", "withAuthor": false }

5. 指令(Directives)

条件控制字段返回:

  • @include(if: Boolean):包含字段
  • @skip(if: Boolean):跳过字段
    query ($showAuthor: Boolean!) {
    book(id: "101") {
      title
      author @include(if: $showAuthor) { name }
    }
    }
    

对比 RESTful API

场景 RESTful 实现 GraphQL 实现
获取书和作者 2 次请求:/book/101 + /author/50 1 次请求:嵌套查询
仅需书名 仍返回完整书对象 仅返回 title 字段
添加书 POST /books + 完整 JSON mutation 指定返回字段

总结

  • 用途:精确数据获取、统一端点、类型安全、实时更新。
  • 操作类型
    • Query:读取数据
    • Mutation:修改数据
    • Subscription:实时推送
  • 高级特性:参数、别名、片段、变量、指令。

工具推荐:使用 Apollo StudioGraphiQL 可视化测试查询。

pigeon接口

(一种跨平台RPC框架‌)在Java中的测试框架,常见的选项包括多种工具和集成方案
基于Pigeon RPC框架的Java测试用例,使用TestNG实现,包含多种参数类型和断言方法

下面是一个基于Pigeon接口的Java测试框架实现,结合TestNG编写,覆盖多种接口参数类型、断言方法(硬断言+软断言),并包含完整可运行的测试用例代码。所有设计均参考了开源项目规范及测试最佳实践。


一、Pigeon接口设计(含多类型参数)

public interface PigeonService {
    // 1. 基本类型参数
    boolean sendMessage(String message, int priority);
    
    // 2. 对象类型参数(自定义DTO)
    MessageStatus sendMessageObject(MessageDTO message);
    
    // 3. 可变参数(支持批量发送)
    int batchSendMessages(String... messages);
    
    // 4. 泛型容器参数
     T processContainer(MessageContainer container);
    
    // 5. 带默认方法的接口(Java 8+)
    default String getServiceVersion() {
        return "v1.2.0";
    }
}

// 支持泛型的容器类
public class MessageContainer {
    private T content;
    private String metadata;
    // 构造方法/getter/setter省略
}

// 消息DTO对象
public class MessageDTO {
    private String id;
    private String content;
    private int retryCount;
    // 构造方法/getter/setter省略
}

// 消息状态枚举
public enum MessageStatus {
    SENT, FAILED, PENDING
}

二、TestNG测试用例实现

import org.testng.annotations.Test;
import org.testng.Assert;
import org.testng.asserts.SoftAssert;
import static org.testng.Assert.assertEqualsNoOrder;

public class PigeonServiceTest {
    private final PigeonService pigeonService = new PigeonServiceImpl(); // 实际实现类

    // 1. 测试基本类型参数 + 硬断言
    @Test
    public void testSendMessageBasicParams() {
        boolean result = pigeonService.sendMessage("Urgent Alert", 1);
        Assert.assertTrue(result, "消息发送失败"); // 硬断言1
        Assert.assertEquals(pigeonService.getServiceVersion(), "v1.2.0", "版本不匹配"); // 硬断言2
    }

    // 2. 测试对象参数 + 多重断言
    @Test
    public void testSendMessageObject() {
        MessageDTO msg = new MessageDTO("ID-2025", "Test Content", 3);
        MessageStatus status = pigeonService.sendMessageObject(msg);
        
        // 使用软断言收集多个失败
        SoftAssert softAssert = new SoftAssert();
        softAssert.assertNotNull(status, "返回状态为null"); 
        softAssert.assertEquals(status, MessageStatus.SENT, "消息状态非SENT");
        softAssert.assertAll(); // 统一报告断言结果
    }

    // 3. 测试可变参数 + 容器操作
    @Test
    public void testBatchSendWithVarargs() {
        String[] messages = {"msg1", "msg2", "msg3"};
        int successCount = pigeonService.batchSendMessages(messages);
        
        Assert.assertTrue(successCount > 0, "成功计数应为正数");
        Assert.assertEquals(successCount, messages.length, "实际发送数量不符");
    }

    // 4. 测试泛型容器 + 异常断言
    @Test(expectedExceptions = IllegalArgumentException.class)
    public void testProcessContainerWithInvalidType() {
        MessageContainer<Integer> container = new MessageContainer<>(123, "meta");
        pigeonService.processContainer(container); // 预期抛出异常
    }

    // 5. 综合测试:混合参数类型 + 集合断言
    @Test
    public void testComplexScenario() {
        MessageDTO msg1 = new MessageDTO("ID-1", "Hello", 1);
        MessageDTO msg2 = new MessageDTO("ID-2", "World", 2);
        
        pigeonService.sendMessageObject(msg1);
        pigeonService.sendMessageObject(msg2);
        int count = pigeonService.batchSendMessages("foot", "bar");
        
        // 验证集合结果
        List<Integer> results = Arrays.asList(2, count);
        assertEqualsNoOrder(results.toArray(), new Integer[]{2, 2}, "结果顺序无关校验");
    }
}

三、关键技术与最佳实践

  1. 参数类型覆盖策略

    • 基本类型(String, int) → 验证边界值
    • DTO对象 → 测试对象完整性
    • 可变参数(String...) → 测试空/多参数场景
    • 泛型容器 → 验证类型安全
  2. 断言选择逻辑

    场景 断言类型 示例方法
    关键路径验证 硬断言 Assert.assertTrue()
    多条件非阻塞验证 软断言 SoftAssert.assertAll()
    集合内容无序校验 顺序无关断言 assertEqualsNoOrder()
    异常流程 异常注解 @Test(expectedExceptions=)
  3. Pigeon集成注意

    • 消息协议一致性:需与Pigeon的消息结构对齐(参考MessageDTO设计)
    • 异步操作:若Pigeon为消息队列,需加入异步结果等待逻辑(如Thread.sleep或轮询)
    • 资源清理:在@AfterMethod中重置消息队列状态

四、执行结果示例(TestNG报告)

PASSED: testSendMessageBasicParams
PASSED: testBatchSendWithVarargs
FAILED: testSendMessageObject
java.lang.AssertionError: 以下断言失败:

  1. 预期 [SENT] 但实际是 [FAILED]

    SKIPPED: testProcessContainerWithInvalidType (因前置失败)

调试建议

  • 软断言适合非致命性检查(如UI多字段校验)
  • 硬断言用于阻断性错误(如数据库连接失败)
  • 使用TestNGdependsOnMethods管理用例依赖关系

完整代码可参考:Pigeon官方GitHubTestNG文档

import com.meituan.pigeon.client.Client;
import org.testng.annotations.*;
import static org.testng.Assert.*;

public class PigeonApiTest {
    private UserService userService; // Pigeon接口示例
    @BeforeClass
    public void setup() {
        // 初始化Pigeon服务引用
        userService = Client.getProxy("com.example.UserService");
    }

    // 基础参数测试
    @Test
    public void testBasicParams() {
        String result = userService.sayHello("World");
        assertEquals(result, "Hello World", "基础字符串参数验证失败");
    }

    // 对象参数测试
    @Test
    public void testObjectParam() {
        User user = new User(1, "Alice");
        boolean created = userService.createUser(user);
        assertTrue(created, "用户对象创建失败");
    }

    // 集合参数测试
    @Test
    public void testCollectionParams() {
        List<Integer> ids = Arrays.asList(1, 2, 3);
        List<User> users = userService.batchGetUsers(ids);
        assertNotNull(users, "批量查询返回null");
        assertEquals(users.size(), 3, "返回用户数量不匹配");
    }

    // 异常情况测试
    @Test(expectedExceptions = IllegalArgumentException.class)
    public void testInvalidParam() {
        userService.getUserById(-1); // 应抛出异常
    }

    // 数据驱动测试
    @DataProvider(name = "userData")
    public Object[][] provideUserData() {
        return new Object[][]{
            {1, "admin"},
            {2, "guest"}
        };
    }

    @Test(dataProvider = "userData")
    public void testDataDriven(int id, String name) {
        User user = userService.getUserById(id);
        assertEquals(user.getName(), name, "数据驱动测试失败");
    }

    // 异步调用测试
    @Test
    public void testAsyncCall() throws Exception {
        Future<User> future = userService.asyncGetUser(1);
        User user = future.get(3, TimeUnit.SECONDS);
        assertNotNull(user, "异步调用超时或失败");
    }
}

RestAssured框架

RestAssured的GitHub官方仓库地址为:‌https://github.com/rest-assured/rest-assured‌‌。该仓库包含以下核心资源:

  1. 主仓库内容
    • 完整源代码、Maven/Gradle依赖配置(如 io.rest-assured:rest-assured:5.5.1)‌
    • 版本更新记录及问题追踪(Issues/Pull Requests)
  2. 官方文档
    • 入门指南‌(Getting Started):提供依赖配置和基础示例‌
    • 详细用法‌(Usage):涵盖DSL语法、HTTP方法(POST/GET/PUT/DELETE等)、响应验证及高级功能‌1
    • JSON Schema验证‌:支持结构化响应校验‌
  3. 特色模块
    • spring-mock-mvc:支持Spring MVC控制器的单元测试‌
    • 数据驱动测试工具(如与TestNG/JUnit集成方案)‌

使用restAssured+TestNG编写Java测试用例

import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import io.restassured.response.Response;
import org.pigeon.test.PigeonTest;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.*;

public class APITestSuite {
    @BeforeClass
    public void setup() {
        RestAssured.baseURI = "https://api.example.com";
        PigeonTest.init(); // 初始化Pigeon测试框架
    }

    // 1. GET请求测试
    @Test
    public void testGetUserById() {
        given()
            .pathParam("userId", 123)
        .when()
            .get("/users/{userId}")
        .then()
            .statusCode(200)
            .body("id", equalTo(123))
            .body("name", not(emptyString()));
    }

    // 2. POST请求测试(JSON body)
    @Test
    public void testCreateUser() {
        String requestBody = "{\"name\":\"John\",\"age\":30}";
        
        given()
            .contentType(ContentType.JSON)
            .body(requestBody)
        .when()
            .post("/users")
        .then()
            .statusCode(201)
            .body("id", notNullValue())
            .body("createdAt", containsString("2025-07-27"));
    }

    // 3. 带查询参数的GET请求
    @Test
    public void testSearchUsers() {
        given()
            .queryParam("name", "John")
            .queryParam("age", "30")
        .when()
            .get("/users/search")
        .then()
            .statusCode(200)
            .body("size()", greaterThan(0));
    }

    // 4. PUT请求测试
    @Test
    public void testUpdateUser() {
        String updateBody = "{\"age\":31}";
        
        given()
            .pathParam("userId", 123)
            .contentType(ContentType.JSON)
            .body(updateBody)
        .when()
            .put("/users/{userId}")
        .then()
            .statusCode(200)
            .body("age", equalTo(31));
    }

    // 5. DELETE请求测试
    @Test
    public void testDeleteUser() {
        given()
            .pathParam("userId", 123)
        .when()
            .delete("/users/{userId}")
        .then()
            .statusCode(204);
    }

    // 6. 使用Pigeon进行数据驱动测试
    @PigeonTest(dataProvider = "userData")
    public void testDataDriven(String name, int age) {
        String requestBody = String.format("{\"name\":\"%s\",\"age\":%d}", name, age);
        
        given()
            .contentType(ContentType.JSON)
            .body(requestBody)
        .when()
            .post("/users")
        .then()
            .statusCode(201);
    }

    // 7. 验证响应时间
    @Test
    public void testResponseTime() {
        given()
        .when()
            .get("/users/123")
        .then()
            .statusCode(200)
            .time(lessThan(2000L)); // 响应时间应小于2秒
    }

    // 8. 验证响应头
    @Test
    public void testResponseHeaders() {
        given()
        .when()
            .get("/users/123")
        .then()
            .statusCode(200)
            .header("Content-Type", containsString("application/json"))
            .header("Cache-Control", not(emptyString()));
    }

    // 9. 文件上传测试
    @Test
    public void testFileUpload() {
        given()
            .multiPart("file", new File("test.txt"))
        .when()
            .post("/upload")
        .then()
            .statusCode(200)
            .body("fileUrl", notNullValue());
    }

    // 10. 认证测试
    @Test
    public void testAuthenticatedRequest() {
        given()
            .auth().basic("username", "password")
        .when()
            .get("/secure/data")
        .then()
            .statusCode(200);
    }
}
    //5.异常场景测试
    @Test
    public void testInvalidRequest(){
        given()
            .queryParam("page",-1)
        .when()
            .get("/products")
        .then()
            .statusCode(400)
            .body("errorCode",equalTo("INVALID_PARAM"));
    }

正向代理与反向代理的本质区别

维度 正向代理 反向代理
代理对象 代理客户端(用户端)13 代理服务端(服务器端)
配置方 客户端主动配置代理地址610 服务端部署,客户端无感知
核心目的 隐藏客户端IP、突破访问限制16 负载均衡、隐藏服务器架构

二、Charles中的配置方法

1. 正向代理配置‌(代理客户端)

步骤‌:

  1. 启用代理:Proxy > Proxy Settings

    • 勾选Enable transparent HTTP proxying
    • 默认端口8888(可修改)
  2. 客户端设置:在浏览器/系统网络中配置代理服务器地址为Charles所在IP:8888

  3. 抓包示例‌:用户浏览器 → Charles(8888端口) → 目标网站(如google.com)

    目标网站看到的是Charles的IP,而非用户真实IP

2. 反向代理配置‌(代理服务端)

步骤‌:

  1. 启用反向代理:Tools > Reverse Proxy
    • Local Port:本地监听端口(如9000
    • Remote Host:目标服务器地址(如example.com:80
    • Preserve Host Header:保持原始Host头(需特定场景启用)
    • Rewrite Redirects:自动重写重定向地址(默认开启)
  2. 勾选Rewrite Host Header(重写Host头)
  3. 访问流程‌:用户访问 http://localhost:9000 → Charles转发请求 → 真实服务器example.com

专业级工具(支持HTTPS/高级功能)

  1. HttpCanary(小黄鸟/黄鸟抓包)
    • 支持协议:HTTP/HTTPS/WebSocket/TCP/UDP
    • 核心功能:实时流量分析、数据注入修改、断点调试、JSON/图片/音频预览
    • 特点:无需Root,但Android 7.0+需手动配置证书;插件扩展能力强,适合开发调试与安全测试‌
    • 新版名称:部分版本已更名为 ‌Reqable‌(功能延续)‌
  2. Packet Capture(无Root抓包)
    • 自动配置本地VPN及CA证书,免Root抓取HTTPS流量
    • 简化操作:一键抓包,支持按应用过滤,数据可导出为PCAP文件‌
    • 适合场景:快速排查应用网络问题,对新手友好‌
  3. ProxyPin(伏羲)
    • 开源工具,支持跨平台协同(手机与PC端联动)
    • 功能亮点:实时修改请求/响应、脚本注入,配合JustTrustMe模块绕过SSL Pinning‌
    • 定位:轻量化但功能全面,适合移动开发联调‌

轻量级工具(基础抓包/快速分析)

  1. 抓包精灵
    • 专注HTTP/HTTPS抓包,界面简洁
    • 支持数据包搜索、过滤及内容预览(如表单/文本)‌
    • 优势:体积小,响应速度快,适合基础调试‌
  2. NetKeeper(SSL抓包神器)
    • 针对HTTPS流量优化,可解析加密数据
    • 特色:悬浮窗实时监控,后台抓包不中断操作‌
    • 注意:高版本安卓兼容性可能受限‌

特殊需求工具

  • ‌Fiddler Everywhere / Charles Proxy

    • 桌面端主流工具(支持Windows/macOS/Linux),通过代理实现手机抓包
  • 适用场景:需深度分析复杂网络交互,如API调试、性能优化‌

    • 安卓配置:需手动设置代理IP及端口,并安装CA证书‌

选型建议表

需求场景 推荐工具 关键优势
免Root抓HTTPS Packet Capture 自动证书配置,操作简单
高级调试/注入 HttpCanary (Reqable) 支持断点修改、多协议解析
开源协作/跨平台 ProxyPin 开源免费,PC与手机协同
快速基础排查 抓包精灵 轻量化,响应迅速
桌面端联动分析 Fiddler/Charles 功能全面,适合复杂场景

💡 ‌注意事项‌:

  • HTTPS抓包需信任工具生成的CA证书,部分银行/金融类App可能触发证书校验失败‌
  • 安卓高版本(如10+)若抓包异常,可尝试关闭“专用DNS”或“私人DNS”设置‌

文章作者: 读序
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 读序 !
  目录