45、CORBA开发与远程调试全解析

CORBA开发与远程调试全解析

1. CORBA相关介绍

1.1 osfind命令与Visibroker

当执行osfind命令时,它可定位 CreditRating_poa 服务器。若连接到网络,osfind程序会尝试定位子网中的所有Visibroker智能代理。若网络中同一端口有其他Visibroker智能代理运行,osfind会返回已注册的服务器对象。JBuilder附带了Visibroker控制台应用程序,该程序位于Visibroker的bin子目录下,可显示不同的智能代理以及它们正在运行的CORBA对象。

1.2 构建CORBA客户端

构建CORBA客户端和构建CORBA服务器一样简单,JBuilder提供了生成客户端应用程序代码的向导。创建CORBA客户端应用程序的典型步骤如下:
1. 初始化CORBA ORB。
2. 创建CORBA客户端存根的实例。
3. 定位CORBA服务器。
4. 将CORBA服务器绑定到客户端存根。
5. 调用CORBA客户端存根上的方法。

JBuilder通过提供一个向导简化了CORBA客户端代码,该向导会在客户端存根文件周围创建一个包装类。使用JBuilder向导,创建CORBA客户端应用程序的修改步骤如下:
1. 生成CORBA客户端类包装器。
2. 创建CORBA客户端类包装器的实例。
3. 调用CORBA客户端类包装器上的方法。

类包装器可以通过两种不同的方式创建:
- 第一种方法是使用对象库创建CORBA客户端接口。
- 第二种方法是在向导菜单下选择“使用CORBA接口”选项,该方法会进一步创建一个实例变量并将导入添加到客户端应用程序中。

1.3 生成CORBA客户端类包装器

CORBA客户端类包装器封装了来自IDL的客户端存根,并添加了ORB初始化以及将类绑定到服务器对象的功能。生成客户端类包装器的步骤如下:
1. 从菜单中选择“文件” -> “新建”,显示对象库。
2. 在对象库的CORBA选项卡中选择“CORBA客户端接口”,这将启动如图所示的向导。

CORBA客户端接口向导包含以下字段:
| 字段 | 描述 |
| ---- | ---- |
| IDL文件 | 包含要包装的接口的IDL文件的位置 |
| 包 | 客户端包装类所属的包的名称 |
| 类 | 将从IDL文件和所选接口生成的类的名称 |
| 接口 | IDL文件中要生成到类中的接口的名称 |
| 生成头注释 | 将项目信息设置添加到生成的类文件中 |

以下是生成的封装接口的代码示例:

package corbaexample.client;
import java.awt.*;
import org.omg.CORBA.*;
public class CreditRatingClientImpl {
    private boolean bInitialized = false;
    private dbCorba.CreditRating _creditRating;
    private com.borland.cx.OrbConnect orbConnect1;
    private String _name = “CreditRating”;
    public CreditRatingClientImpl() {
        try  {
            jbInit();
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    private void jbInit() throws Exception {
    }
    public boolean init() {
        if (!bInitialized) {
            try {
                org.omg.CORBA.ORB orb = null;
                if (orbConnect1 != null) {
                    orb = orbConnect1.initOrb();
                }
                if (orb == null) {
                    orb = org.omg.CORBA.ORB.init((String[])null, 
                        System.getProperties());
                }
                _creditRating = dbCorba.CreditRatingHelper.bind(orb, 
                    “/” + _name + “_poa”, _name.getBytes());
                bInitialized = true;
            }
            catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        return bInitialized;
    }
    public dbCorba.CreditRating getCorbaInterface() {
        return _creditRating;
    }
    public void setCorbaInterface(dbCorba.CreditRating intf) {
        _creditRating = intf;
    }
    public com.borland.cx.OrbConnect getORBConnect() {
        return orbConnect1;
    }  
    public void setORBConnect(com.borland.cx.OrbConnect orbConnect) {
        this.orbConnect1 = orbConnect;
    }
    public short getCreditScore(String name, 
        org.omg.CORBA.IntHolder creditscore)  {
        init();
        return _creditRating.getCreditScore(name, creditscore);
    }
}

生成的代码实现了CORBA接口Bean,并创建了IDL文件中定义的所有方法。每个方法在返回客户端存根的方法调用之前都会调用 init() 方法,该方法用于初始化ORB、定位服务器对象并将客户端存根实例绑定到服务器。

1.4 创建CORBA客户端类包装器的实例

要使用CORBA客户端类包装器,可以手动将导入和实例变量添加到客户端应用程序中,也可以在JBuilder主菜单中选择“向导” -> “使用CORBA接口”。具体步骤如下:
1. 选择需要访问CORBA服务器的客户端应用程序文件。
2. 加载该文件后,从菜单中选择“向导” -> “使用CORBA接口”,会出现一个对话框,询问CORBA接口Bean是否已经存在。
- 如果CORBA接口Bean不存在,向导的第二步与之前的向导类似,只是窗口标题栏显示为“步骤2 of 3”。
- 如果Bean已经创建,勾选该选项,向导将跳转到最后一步,询问作为CORBA接口Bean的类以及要创建的实例变量的字段名称。

完成“使用CORBA接口”向导后,定义的实例变量将被添加到类中,并且导入会更新以包含CORBA接口Bean的包。

1.5 调用CORBA客户端类包装器上的方法

要开始使用CORBA客户端类包装器,只需使用创建的实例变量,就像使用任何其他对象一样。例如,以下是调用服务器对象的 getCreditScore 方法的示例代码:

org.omg.CORBA.IntHolder iData = new org.omg.CORBA.IntHolder();
myCreditRating.getCreditScore(“Michael Landy”, iData);

第一行代码创建了调用方法所需的参数,下一行使用通过“使用CORBA接口”向导创建的实例变量并执行 getCreditScore 方法。

1.6 实践操作:创建返回课程信息列表的CORBA服务器

步骤1:打开示例并查看类
  1. 从JBuilder菜单中选择“文件” -> “打开项目”。
  2. 找到 jbbook/ch25/swing/ChalkTalk.jpx 并点击“确定”。
  3. 项目将在项目面板中打开,打开位于 userservices 文件夹中的 CommonOperations.java 文件。
  4. 双击 CommonOperations.java ,内容面板将显示该文件。
步骤2:定位 browseCourses 方法

搜索文件并定位 browseCourses 方法,当前代码如下:

public java.util.Collection browseCourses(CourseFilter courseFilter) {
    Vector v = new Vector();
    v.add(
        new CourseData(
            “Introduction to Java”,
            “CS211”,
            “This course will teach you the fundamentals of Java using JDK 1.4. Topics include File I/O, AWT and Swing. Student must have a grade of C or better in CS202 to register for this course. Students who have obtained a B or higher grade in CS201 may take this class with Instructor’s permission. There will be one mid - term and one final exam with one substantial programming assignment.”)
    );
    v.add(
        new CourseData(
            “Introduction to JBuilder”,
            “CS251”,
            “This course will teach you how to use JBuilder to develop Java applications. Topics include developing, JavaBeans, JSPs and Servlets. Student must have a grade of C or better in CS211 to register for this course. There will be one mid - term and one final exam with one substantial programming assignment.”)
    );
    return Collections.unmodifiableCollection(v);
}

该方法创建了一个向量,静态地向向量中添加数据,然后将一个集合返回给调用方法。

步骤3:创建IDL文件
  1. 从主菜单中选择“文件” -> “新建”,在对象库的CORBA选项卡中选择“示例IDL”,准备好后点击“确定”。
  2. 示例IDL向导会询问包和IDL文件名,将“包”字段设置为 chalktalk ,“文件名”字段设置为 course.idl
  3. course.idl 文件将显示在内容面板中,删除文件内容并添加以下代码:
module course {
    typedef sequence<string> StringData;
    interface CourseDataCorba {
        string getCourseList(inout StringData names, inout StringData coursenumber, inout StringData description);
    };
};
  1. 保存文件。
  2. 在项目面板中右键单击文件名,从弹出菜单中选择“make”来编译 course.idl 文件,编译后将创建以下文件:
    • CourseDataCorbaStub.java
    • CourseDataCorbaPOA.java
    • CourseDataCorba.java
    • CourseDataCorbaPOATie.java
    • CourseDataCorbaHelper.java
    • StringDataHelper.java
    • CourseDataCorbaHolder.java
    • StringDataHolder.java
    • CourseDataCorbaOperations.java
步骤4:实现服务器代码

使用CORBA服务器应用程序向导创建服务器应用程序,步骤如下:
1. 选择“文件” -> “新建”,在对象库中选择“CORBA服务器应用程序”并点击“确定”。
2. 在显示的CORBA服务器应用程序向导中,将IDL文件设置为 course.idl ,包设置为 chalktalk ,勾选“生成带有监视器的可见应用程序”和“生成头注释”选项,然后点击“下一步”。
3. 向导的下一步是创建运行时配置,勾选“创建运行时配置”框,将名称设置为“Server Application”。
4. 展开项目面板中的 chalktalk.course.server 树。
5. 选择 CourseDataCorbaImpl.java 类,双击该类将其加载到内容面板中,该类将包含服务器实现。
6. 定位 getCourseList 方法,并将其修改为以下内容:

public String getCourseList(chalktalk.course.StringDataHolder names,
    chalktalk.course.StringDataHolder coursenumber,
    chalktalk.course.StringDataHolder description)  {
    String sCourse[] = new String[2];
    String sCourseNumber[] = new String[2];
    String sCourseDescription[] = new String[2];
    sCourse[0] = “Introduction to JBuilder”;
    sCourseNumber[0] = “CS251”;
    sCourseDescription[0] = “Course description is coming from a CORBA Server”;
    sCourse[1] = “Introduction to Java”;
    sCourseNumber[1] = “CS211”;
    sCourseDescription[1] = “Intro to Java - Course description from a CORBA Server”;
    names.value = sCourse;
    coursenumber.value = sCourseNumber;
    description.value = sCourseDescription;
    ServerMonitor.log(“(“ + _name + “) CourseDataCorbaImpl.java getCourseList()”);
    return “”;
}

该方法创建了字符串数组并填充数据,然后将这些数组设置到 StringDataHolder 类的 value 属性中。
7. 保存文件。
8. 从菜单中选择“工具” -> “Visibroker智能代理”,启动命名服务。
9. 右键单击 courseServerApp.java 文件,选择“使用服务器应用程序配置运行”。如果信息输入正确,将看到一个对话框显示课程服务器正在运行。

步骤5:实现客户端代码
  1. 在内容面板中打开 CommonOperations.java 文件。
  2. 从菜单中选择“向导” -> “使用CORBA接口”。
  3. 在显示的“使用CORBA接口向导 - 步骤1 of 3”对话框中,确保“CORBA接口Bean已经存在”复选框未被勾选,然后点击“下一步”。
  4. 下一个对话框显示“步骤2 of 3”,将字段设置为指定的值。
  5. 点击“下一步”。
  6. 出现“使用CORBA接口向导”的最终对话框,将“字段”值设置为 myCourseDataCorba
  7. 点击“完成”。
  8. CommonOperations.java 类的导入部分添加以下行:
import chalktalk.course.StringDataHolder;
  1. browseCourses 方法重写为以下内容:
public java.util.Collection browseCourses(CourseFilter courseFilter) {
    Vector v = new Vector();
    int x;
    StringDataHolder coursenames = new StringDataHolder();
    StringDataHolder coursenumbers = new StringDataHolder();
    StringDataHolder coursedescriptions = new StringDataHolder();
    // Needed so as not to pass a null pointer (Not CORBA accepted)
    coursenames.value = new String[0];
    coursenumbers.value = new String[0];
    coursedescriptions.value = new String[0];
    myCourseDataCorba.getCourseList(coursenames,coursenumbers, coursedescriptions);
    for(x = 0;x < coursenames.value.length;x++) {
        v.add(
            new CourseData(
                coursenames.value[x],
                coursenumbers.value[x],
                coursedescriptions.value[x])
        );
    }
    return Collections.unmodifiableCollection(v);
}

修改后的 browseCourses 方法调用CORBA客户端包装器来定位服务器对象,从服务器对象中检索数据并将其读入集合数组。

步骤6:执行应用程序

要运行ChalkTalk应用程序,需要确保Visibroker智能代理正在运行、课程服务器正在运行,最后运行ChalkTalk应用程序。具体步骤如下:
1. 启动Visibroker智能代理,从菜单中选择“工具” -> “Visibroker智能代理”。
2. 右键单击 courseServerApp.java ,选择“使用服务器应用程序配置运行”。
3. 从菜单中选择“运行” -> “运行项目”,显示“选择运行时配置”对话框,选择“ChalkTalkClient”配置。
4. 从客户端应用程序中浏览课程,课程描述将来自CORBA服务器。

1.7 总结

JBuilder的功能已更新以支持Visibroker和其他符合CORBA的ORB,它使得从接口定义文件生成可用作服务器或客户端的代码变得更加容易。

1.8 思考

  • CORBA提供了哪些好处?
  • CORBA如何有利于n层设计?
  • 在哪些情况下更有可能选择一种分布式技术而不是另一种?

1.9 流程图:创建CORBA服务器和客户端流程

graph LR
    classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
    classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px

    A([开始]):::startend --> B(打开示例项目):::process
    B --> C(定位browseCourses方法):::process
    C --> D(创建IDL文件):::process
    D --> E(实现服务器代码):::process
    E --> F(实现客户端代码):::process
    F --> G(执行新代码):::process
    G --> H([结束]):::startend

2. 远程调试解释

2.1 远程调试的概念

远程调试允许你调试在另一台机器上运行的应用程序。随着应用服务器的普及,跨网络调试应用程序的能力变得越来越重要。不过,远程调试不一定非要跨网络,应用程序也可以在同一台机器上但运行在完全不同的Java虚拟机(JVM)中。在本文中,远程调试将以在两台不同的机器上进行为例进行说明。

2.2 应用服务器与远程调试的需求

应用服务器通常为构建分布式应用程序提供上下文和一组服务,例如管理数据库事务、数据库资源池和用户安全权限等服务。在每个开发人员的机器上安装和配置应用服务器并不能解决问题,因为应用程序是在应用服务器的上下文中运行的。因此,为了调试代码,通常需要记录操作并在应用程序执行后进行审查。

应用程序在应用服务器内运行的情况如下:应用代码利用了应用服务器的数据库池和安全上下文。除了调试在应用服务器中运行的代码外,还可能需要调试在不同平台上运行的代码,因为这些代码可能会使用仅在该机器上可用的资源。

2.3 Java平台调试器架构(JPDA)

在Java 2中,Sun引入了Java平台调试器架构(JPDA),其目的是允许编写Java调试工具而无需考虑平台的具体细节,如硬件、操作系统和虚拟机实现。

JPDA由一个协议、两个接口和对Java虚拟机的更改组成。该协议描述了网络上调试信息的格式,Java虚拟机必须实现Java虚拟机调试接口(JVMDI)。不同机器上的JDK版本不需要匹配,只要它们都实现了JPDA,就可以跨网络、在不同平台上使用不同版本的Java对应用程序代码进行调试。

JBuilder实现了符合JPDA的调试器,满足了不考虑平台具体细节进行调试的规范。如之前所述,JBuilder拥有强大的调试器,可以设置断点、监视变量和修改值。

2.4 设置调试环境

为了支持远程调试,虚拟机(VM)需要传递命令行选项来开启该VM的调试选项。一个VM作为服务器监听调试连接,另一个VM作为客户端连接到服务器VM。客户端VM需要了解服务器VM才能协商调试连接,VM的命令行选项用于设置连接属性。以下是常见的命令行选项列表:
| 选项 | 描述 |
| ---- | ---- |
| -classic | 某些VM可能不支持此选项,该选项选择经典VM而非即时编译VM |
| -Xdebug | 启用VM中的调试功能 |
| -Xnoagent | 禁用sun.tools.debug中包含的原始调试代理,JPDA有自己的调试代理 |
| -Djava.compiler=NONE | 禁用JIT编译器,调试通常需要禁用JIT编译器 |
| -Xrunjdwp: | 加载JPDA实现,子选项控制调试器的工作方式 |

需要注意的是,VM通常应禁用即时编译(JIT)编译器,在大多数情况下,这是正确调试应用程序所必需的。不过,JDK 1.4.1现在支持全速调试和其他新增强功能。

2.5 流程图:远程调试流程

graph LR
    classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
    classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px

    A([开始]):::startend --> B(设置服务器VM调试选项):::process
    B --> C(设置客户端VM调试选项):::process
    C --> D(客户端VM连接服务器VM):::process
    D --> E(进行远程调试):::process
    E --> F([结束]):::startend

综上所述,本文详细介绍了CORBA开发的相关内容,包括构建CORBA客户端、生成类包装器、实践操作创建CORBA服务器和客户端等,同时也阐述了远程调试的概念、需求以及设置调试环境的方法。通过这些内容,可以更好地进行分布式应用程序的开发和调试工作。

3. 调试远程应用程序

3.1 远程调试的准备工作

在进行远程调试之前,需要确保已经正确设置了调试环境,即服务器VM和客户端VM都已经配置好了相应的命令行选项。具体来说,服务器VM需要设置为监听调试连接,客户端VM需要知道服务器VM的地址和端口信息。

3.2 建立调试连接

客户端VM通过配置好的命令行选项,尝试连接到服务器VM。一旦连接成功,就可以开始进行远程调试。在JBuilder中,可以使用其强大的调试器功能,如设置断点、监视变量和修改值等。

3.3 调试过程示例

假设我们有一个在服务器VM上运行的Java应用程序,需要进行远程调试。以下是一个简单的示例,展示如何在JBuilder中进行远程调试:
1. 设置服务器VM的命令行选项 :在服务器VM的启动命令中添加调试选项,例如:

java -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000 YourMainClass

这里的 address=8000 表示服务器VM监听的调试端口为8000。

  1. 设置客户端VM的命令行选项 :在客户端VM的启动命令中添加连接服务器VM的选项,例如:
java -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=n,address=remote_host:8000 YourClientClass

这里的 remote_host 是服务器VM的主机名或IP地址。

  1. 在JBuilder中配置远程调试

    • 打开JBuilder,选择“运行” -> “调试配置”。
    • 在调试配置对话框中,选择“远程Java应用程序”。
    • 配置服务器VM的主机名、端口号和要调试的类。
    • 点击“确定”保存配置。
  2. 启动远程调试 :在JBuilder中点击“调试”按钮,JBuilder将尝试连接到服务器VM。如果连接成功,就可以开始进行调试,例如设置断点、单步执行代码等。

3.4 注意事项

  • 确保服务器VM和客户端VM的网络连接正常,防火墙允许调试端口的通信。
  • 检查服务器VM和客户端VM的JDK版本是否都实现了JPDA,虽然版本可以不同,但都需要支持JPDA。
  • 在调试过程中,如果遇到问题,可以查看服务器VM和客户端VM的日志文件,以获取更多的调试信息。

4. 跨进程断点

4.1 跨进程断点的概念

跨进程断点是指在多个进程之间设置断点,以便在不同进程的代码执行到特定位置时暂停程序的执行。在分布式应用程序中,不同的组件可能运行在不同的进程中,使用跨进程断点可以方便地调试这些组件之间的交互。

4.2 在JBuilder中设置跨进程断点

在JBuilder中设置跨进程断点的步骤如下:
1. 打开多个项目 :如果要调试的组件分别位于不同的项目中,需要在JBuilder中打开这些项目。
2. 设置断点 :在每个项目的代码中设置需要的断点。
3. 启动调试 :启动所有相关的进程,并开始调试。当代码执行到设置的断点时,程序将暂停执行,允许你查看变量的值、单步执行代码等。

4.3 跨进程断点的应用场景

跨进程断点在以下场景中非常有用:
- 分布式系统调试 :在分布式系统中,不同的服务可能运行在不同的进程中,使用跨进程断点可以方便地调试这些服务之间的交互。
- 多线程调试 :在多线程应用程序中,不同的线程可能运行在不同的进程中,跨进程断点可以帮助你调试线程之间的同步和通信问题。

4.4 流程图:跨进程断点调试流程

graph LR
    classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
    classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px

    A([开始]):::startend --> B(打开多个项目):::process
    B --> C(在各项目代码中设置断点):::process
    C --> D(启动所有相关进程):::process
    D --> E(开始调试):::process
    E --> F{是否到达断点}:::decision
    F -->|是| G(暂停程序执行,进行调试操作):::process
    G --> H{是否继续调试}:::decision
    H -->|是| E
    H -->|否| I([结束]):::startend
    F -->|否| E

5. 应用服务器的配置设置

5.1 应用服务器与调试的关系

应用服务器为分布式应用程序提供了运行环境,在调试分布式应用程序时,需要对应用服务器进行适当的配置,以确保调试功能能够正常工作。

5.2 常见应用服务器的调试配置

以下是一些常见应用服务器的调试配置示例:
| 应用服务器 | 调试配置方法 |
| ---- | ---- |
| Tomcat | 在 catalina.sh (Linux)或 catalina.bat (Windows)中添加调试选项,例如: JAVA_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n" |
| JBoss | 在 standalone.conf (Linux)或 standalone.conf.bat (Windows)中添加调试选项,例如: JAVA_OPTS="$JAVA_OPTS -Xdebug -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n" |
| WebLogic | 在 startWebLogic.sh (Linux)或 startWebLogic.cmd (Windows)中添加调试选项,例如: JAVA_OPTIONS="-Xdebug -Xrunjdwp:transport=dt_socket,address=8453,server=y,suspend=n" |

5.3 配置注意事项

  • 端口冲突 :确保调试端口没有被其他应用程序占用,避免端口冲突。
  • 权限问题 :确保调试用户具有足够的权限来启动应用服务器和进行调试操作。
  • 环境变量 :在配置调试选项时,要注意环境变量的设置,确保应用服务器能够正确识别和使用这些选项。

5.4 流程图:应用服务器调试配置流程

graph LR
    classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px

    A([开始]):::startend --> B(选择应用服务器):::process
    B --> C(查找配置文件):::process
    C --> D(添加调试选项):::process
    D --> E(保存配置文件):::process
    E --> F(启动应用服务器):::process
    F --> G([结束]):::startend

6. 总结与展望

6.1 总结

本文全面介绍了CORBA开发和远程调试的相关知识。在CORBA开发方面,详细阐述了构建CORBA客户端、生成客户端类包装器、创建CORBA服务器和客户端的实践操作步骤。在远程调试方面,解释了远程调试的概念、需求,介绍了设置调试环境、调试远程应用程序、使用跨进程断点以及应用服务器配置设置的方法。

6.2 展望

随着分布式系统的不断发展,CORBA和远程调试技术将继续发挥重要作用。未来,可能会出现更高效、更便捷的开发和调试工具,进一步简化开发和调试过程。同时,对于分布式系统的安全性和性能优化也将成为研究的重点。开发者需要不断学习和掌握新的技术,以应对不断变化的开发需求。

6.3 建议

  • 在进行CORBA开发时,充分利用JBuilder提供的向导和工具,提高开发效率。
  • 在远程调试过程中,注意调试环境的配置和网络连接的稳定性,确保调试工作能够顺利进行。
  • 对于应用服务器的配置,要仔细阅读官方文档,根据实际情况进行合理配置。

通过本文的学习,希望开发者能够更好地掌握CORBA开发和远程调试技术,提高分布式应用程序的开发和调试能力。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值