Java网络编程 - 同步阻塞IO模型

Java网络编程-IO模型篇之从BIO、NIO、AIO到内核select、epoll剖析! 在前面咱们详细叙述了Linux五种IO模型以及Java所提供的三种IO模型支持,对于Java-IO这块内容,阻塞、非阻塞、同步、异步等这些区别就不再聊了,认真看下来本文后自然会有答案,最后是需要重点表明一点: NIO、AIO都是单线程处理多个连接,但并不代表着说永远只有一条线程对网络连接进行处理,这里所谓的单线程处理多个连接,其实本质上是指单条线程接收客户端连接。从上述这段话中应该可以得知:本质上对于客户端的网络连接照样会启动多条线程处理... 阅读详情

由于项目需要使用Java开发后台服务器程序,C/C++程序员就要学学Java了。博客内容用来记录我的学习过程。Unix/Linux下的几种网络IO模型在之前的博客中已经提及到,但是使用的大多数都是Unix/Linux下的系统调用。
博客内容大多数来自网络资料以及书籍《Netty权威指南》,转载请注明出处http://blog.youkuaiyun.com/Robin__Chou/article/details/54378934,谢谢!

如果想了解Unix/Linux下网络IO模型的可以看如下的几篇相关博客,里面大多都是代码段,原理性的讲解比较少。
1.Unix/Linux下5种I/O模型
2.多进程并发服务器
3.多线程并发服务器
4.IO多路复用之select
5.IO多路复用之poll
6.IO多路复用之epoll
Java作为一门跨平台的语言,当然也会支持上述的几种IO模型。在JDK1.4之前的版本中,Java对IO的支持并没有那么好。直到JDK1.4发布,Java才开始支持非阻塞IO,也是这个版本开始逐渐使用Java开发高性能服务器。更是到现在有了像Netty,Mina这样优秀的开源框架。

1.同步阻塞式IO编程

同步阻塞式IO模型中Server端始终有一个“前置”的连接负责和接入进来的客户端。当有客户端接入进来时,就创建一个线程用于和该客户端进行交互,主线程则继续回到accept阻塞状态。
相比使用C/C++开发,Java程序的Socket编程则方便了许多。下面实例一个时间服务器的程序,示例程序设计思路上来源《Netty权威指南》一书。时间服务器的运行流程就是客户端连接到服务器之后,向服务器发送TIME QUERY命令,Server接受到命令后回送服务器当前时间给客户端。程序如下:

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/*
 * BIO模型的问题在于每一个客户端连接进来,服务器端就需要创建一个线程来处理客户端链路
 * 一个线程只能处理一个客户端连接,对于高性能,高并发接入场景很难满足。
 * */
public class TimeServer {
    public static void main(String[] args){
        int port = 8080;                                            //监听端口号为8080

        ServerSocket server = null;
        try {
            server = new ServerSocket(port);
            System.out.println("时间服务器监听端口:" + port);
            Socket socket = null;

            while (true) {
                socket = server.accept();                           //accecpt阻塞等待连接

                //有客户端连接进来则创建处理线程,负责和客户端交互
                new Thread(new TimeServerHandler(socket)).start();
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally {
            if (server != null) {
                System.out.println("服务器端关闭。");
                try {
                    server.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                server = null;
            }
        }
    }
}

处理客户端的线程则可以写成:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class TimeServerHandler implements Runnable {
    private Socket socket;                      //与客户端交互Socket

    public TimeServerHandler(Socket socket) {   //构造函数,传入连接进来的socket
        this.socket = socket;
    }

    @Override
    public void run() {
        BufferedReader in = null;
        PrintWriter out = null;
        try {
            in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
            out = new PrintWriter(this.socket.getOutputStream(), true);
            String currentTime = null;
            String body = null;
            while (true) {
                body = in.readLine();           //读取客户端发送的消息
                if (body == null)               //空消息,则关闭Server端线程
                    break;
                System.out.println("服务器端收到消息:" + body);

                /*
                 * 此处处理客户端请求
                */

                if("QUERY TIME".equalsIgnoreCase(body)){
                    currentTime = new java.util.Date(System.currentTimeMillis()).toString();
                }else{
                    currentTime = "未识别的命令";
                }

                out.println(currentTime);
            }

        } catch (Exception e) {
            //关闭接受流
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
            //关闭发送流
            if (out != null) {
                out.close();
                out = null;
            }
            //关闭socket
            if (this.socket != null) {
                try {
                    this.socket.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
                this.socket = null;
            }
        }
    }
}

客户端程序则比较简单就是建立连接后发送一条QUERY TIME命令即可。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;


public class TimeClient {
    public static void main(String[] args) {
        int port = 8080;
        Socket socket = null;
        BufferedReader in = null;
        PrintWriter out = null;
        try {
            socket = new Socket("127.0.0.1", port); 
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            out = new PrintWriter(socket.getOutputStream(), true);
            out.println("QUERY TIME");
            System.out.println("成功发送命令到服务器端。");     
            String resp = in.readLine();                    //读取服务器端返回信息
            System.out.println("当前时间:" + resp);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭输出流
            if (out != null) {
                out.close();
                out = null;
            }

            //关闭输入数据流
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                in = null;
            }

            //关闭socket连接
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                socket = null;
            }
        }
    }
}

程序分析

同步阻塞式IO在每个客户端连接进来时就创建一个线程来对客户端进行交互,我们知道每个线程是会占资源的,而且创建线程的过程也是要占据资源的。所以对于大量的用户连接使用同步阻塞IO进行服务器端程序设计并不合理。这种模型也难以满足高性能多并发接入的应用场合。

2.使用线程池优化的同步阻塞式IO编程

为了解决同步阻塞模型中一个客户端需要创建一个线程进行交互的问题,人们对其进行优化,后台通过一个线程池来处理多个客户端的求解接入。
和上面不同的就是在客户端连接进来之后,不是去创建一个线程来进行交互,而是直接将客户端的交互任务丢给线程池去完成。JDK的线程池维护一个消息队列和一些活跃的线程来完成消息队列中的任务。线程池是可以设置消息队列的大小和最大的线程数量的,所以,相比之前的情况,这种方式的资源是可控的,不会导致客户端连接过多而导致服务器端资源耗尽。
同样时间服务器作为案例,从代码中可以看出,其实流程上大致相同,不同的就是一个在创建线程进行客户端交互,而另一个是创建一个线程池,客户端连接进来之后,直接将客户端连接丢给线程池去处理。

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

import com.phei.netty.bio.TimeServerHandler;


public class TimeServer {
    public static void main(String[] args){
        //监听端口号为8080
        int port = 8080;                                                
        ServerSocket server = null;
        try {
            server = new ServerSocket(port);
            System.out.println("时间服务器监听端口:" + port);
            Socket socket = null;
            TimeServerHandlerExecutePool singleExecutor 
                = new TimeServerHandlerExecutePool(50, 10000);          // 创建IO任务线程池
            while (true) {
                socket = server.accept();
                singleExecutor.execute(new TimeServerHandler(socket));
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally {
            if (server != null) {
                System.out.println("服务器端关闭。");
                try {
                    server.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                server = null;
            }
        }
    }
}
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


public class TimeServerHandlerExecutePool {

    private ExecutorService executor;

    public TimeServerHandlerExecutePool(int maxPoolSize, int queueSize) {
        executor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),
                maxPoolSize, 120L, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(queueSize));
    }

    public void execute(Runnable task) {
        executor.execute(task);
    }
}

线程池中单个线程的处理还是和同步阻塞式IO模型的Handler相同。客户端没有任何不同。使用线程池优化的同步阻塞式IO编程虽然解决了上述中的创建线程问题,由于引入了一个消息队列,所以如果连接数量一旦很大的时候肯定会导致消息的延迟,如果某些客户端的网络不畅,其他的客户端没办法得到及时的响应。而且在队列满了之后的客户端消息都会被直接拒绝。

3.后记

根据近段时间翻阅的论坛,博客等等地方来看,使用Java来开发后台服务器的案例不在少数,甚至是实时性要求较高的游戏行业也有使用Java开发后台服务器的案例。手机端APP应用后台使用Java开发服务器的就更多了。此外物联网技术中也有使用的案例,特别是常常使用自定义的通信协议的智能家居等。分析使用的原理无外乎有两点:

  • 使用方便,开发简单
  • 容易维护,可靠性高

尤其使用Netty,Mina这样的开源框架,让开发关注于业务,而不被繁琐的技术细节所困扰。

Wafer的T7 Code是什么,怎么来的 在半导体Cim系统中,waferstart 业务需要支持 Sorter 读取 Wafer 的 T7 Code,与 MES 的 Wafer ID 进行绑定,以便向 前追溯 Wafer 历史,那么问题来了,T7code 是什么Wafer的T7 Code是一种,用于标识双面抛光的晶圆。这种标记采用二维矩阵代码符号,适用于各种尺寸的晶圆,不仅限于450mm无缺口晶圆7。T7 Code是衬底来料背面通常会有的供应商刻的追溯ID,这是一串数字和字母组成的流水码。 阅读详情

相关推荐

stata 代码实现熵值法计算 含常见问题解答

2.标准化①正向指标②负向指标。

m0_74825360的博客 1217

同步阻塞io、同步非阻塞io、异步阻塞io和异步非阻塞io详解和区别

同步阻塞 I/O 简单易用,但可能导致性能问题;同步非阻塞 I/O 可以减少阻塞等待的时间,但需要频繁轮询状态;异步非阻塞 I/O 在高并发环境下表现优秀,但编程复杂度较高。

2301_77760093的博客 3595

基于ROS2的无人机野生动物巡查系统设计

自主巡检系统是嵌入式人工智能与机器人技术融合的关键应用方向,其核心在于多源感知融合、实时路径规划与端侧智能识别的协同实现。原理上依赖SLAM定位构建环境地图,结合栅格化建模与混合路径算法(如蚁群+BFS)达成全覆盖遍历;技术价值体现在资源受限平台下的确定性响应与时空对齐能力,支撑高鲁棒性野外作业。典型应用场景包括生态监测、禁飞区巡检及小尺度智能安防。本文围绕ROS2 Humble中间件架构、Livox激光雷达与MaixCAM视觉协同、YOLO11n INT8模型端侧部署等关键技术展开,突出嵌入式AI与实时控

weixin_30598047的博客 649

同步阻塞IO、同步非阻塞IO、异步阻塞(IO多路复用)、异步非阻塞IO区别

常用4种IO模型 (1)同步阻塞IO(Blocking IO):即传统的IO模型。 (2)同步非阻塞IO(Non-blocking IO):默认创建的socket都是阻塞的,非阻塞IO要求socket被设置为NONBLOCK。 注意这里所说的NIO并非Java的NIO(New IO)库。 (3)IO多路复用(IO Multiplexing):即经典的Reactor设计模式,有时也称为异步阻...

datuzijean的博客 4618

避开SM30维护视图的坑:自动更新字段+日志记录的5个关键配置步骤

本文深入解析了SAP ABAP开发中SM30维护视图的深度定制,聚焦于实现自动更新字段与日志记录功能。文章详细阐述了从SE54配置起点到核心编码的5个关键步骤,重点讲解了如何利用事件驱动模型(如05创建事件和01保存前事件)安全操作动态数据结构,并提供了避免常见陷阱的实用技巧,帮助开发者构建稳定可靠的自动化维护机制。

plant的博客 622

计算机模拟爆破过程,基于LSDYNA岩石爆破模拟建模分析

原标题:基于LSDYNA岩石爆破模拟建模分析 Ls Dyna岩石爆破模拟仿真作者:dyna_focus擅长领域:dyna/abaqus/hypermesh一、 数值模型的建立1.1 单元及算法的选择岩石,炸药,空气都采用solid164实体单元。岩石采用常应力实体单元,该种单元属于纯粹的lagrange算法,该种算法单元网格附着在材料上,随着材料的流动而产生单元网格的变形,但是在结构变形巨大时,有...

weixin_29494379的博客 7313

同步IO(阻塞IO、非阻塞IO), 异步IO的理解

用户进程发起请求从内核中获取数据那么这时候有两种情况: 操作系统还没有准备后数据,那么这时候怎么办,有两种方法:         a. 让用于进程等着(这种情况就是阻塞)         b. 如果没有数据就返回一个ERROR,不需要用户进程干等(这种情况就是非阻塞) 过了一会儿操作系统准备好数据了,这时候又有两种方法:         a. 啥也不管,等着用户进程再次来请求才把数据给它(这种情况就是同步)         b. 负责到底,数据准备好,直接给到用户进程,并且还发出一个信号,告诉用户

weixin_44306626的博客 2796

YOLOv12环境配置,手把手教你使用YOLOv12训练自己的数据集和推理(附YOLOv12网络结构图),全文最详细教程

YOLO 系统尊嘟太卷了吧,YOLOv11 还没完明白,YOLOv12 就重磅来袭,YOLOv12 是由纽约州立大学联合中科院在 2025年2月19 日发布,从 YOLOv12 论文题目我们大概就知道做了那些更新 ,下图是YOLOv12 在 COCO 数据集上的性能表现引入区域注意力机制(area-attention):通过引入十字形窗口自我注意机制,沿水平和垂直条纹计算注意力机制,纵横交错的注意机制。如下图所示,区域注意力采用最多简单的等分方法,将特征地图垂直或水平划分为 L 个区域。

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 6万+

IO实现方式(同步阻塞、同步非阻塞、IO多路复用)

同步阻塞io在数据在数据拷贝到两个阶段都是阻塞的,即把socket的数据拷贝到内核缓冲区和把内核缓冲区的数据拷贝到用户态到应用程序缓冲区都是阻塞的。用户线程在这个期间不能处理其他任务。优点:简单易用缺点:为每一次io请求都要分配一个线程,如果获取不到数据会一直阻塞。

weixin_43993064的博客 1722

UWB芯片DW3000之PDOA测向实现源码

本文详细介绍如何基于UWB芯片DW3000之PDOA测向的源码实现。

DT程序员的博客 2613

基于FPGA的BP神经网络Verilog设计与Matlab仿真

BP(Backpropagation)神经网络是一种常见且广泛应用的神经网络类型,通过反向传播算法实现训练和学习。本文将介绍如何使用Verilog语言在FPGA上设计一个简易的BP神经网络,并使用Matlab进行仿真验证。BP神经网络由输入层、隐藏层和输出层组成。在上述Verilog代码中,我们定义了BP神经网络的输入层、隐藏层和输出层的神经元数量,并声明了相应的信号和参数。在上述Verilog代码中,我们定义了BP神经网络的输入层、隐藏层和输出层的神经元数量,并声明了相应的信号和参数。

2301_79326254的博客 559

Java I/O模型解析:阻塞与非阻塞、NIO与NIO2

本文深入探讨了Java I/O模型的四种类型:阻塞与非阻塞I/O、NIO与NIO2。阻塞I/O适用于简单场景,非阻塞I/O适合高并发处理。NIO通过通道和缓冲区提升I/O性能,NIO2进一步优化文件操作。选择合适的I/O模型能显著提高Java应用性能。

silenceallat的博客 1510

信号检测算法的MATLAB仿真:能量检测、循环平稳检测和匹配滤波检测

根据具体的应用场景和要求,选择合适的信号检测算法可以提高系统的性能和可靠性。在无线电通信中,信号检测是一项重要的任务,它用于检测和识别接收到的信号。在本文中,我们将介绍三种常用的信号检测算法:能量检测、循环平稳检测和匹配滤波检测,并提供相应的MATLAB源代码进行仿真实现。在上述代码中,我们首先设置了能量检测的阈值,然后生成了一个包含1000个服从正态分布的随机信号样本的信号。在上述代码中,我们首先设置了信号模板,然后生成了一个包含信号模板的信号样本。最后,通过比较相关性的最大值与阈值来判断信号是否存在。

PixelLancer的博客 1363

Java网络编程(二) —— 网络IO模型及同步异步、阻塞非阻塞最通俗讲解

文章目录1. 烧水举例同步和异步:阻塞和非阻塞:2. Unix网络IO模型1. 阻塞IO模型:2. 非阻塞IO模型I/O复用模型信号驱动式I/O异步IO模型 参考文章 https://www.zhihu.com/question/19732473 https://juejin.im/entry/5a72d7f36fb9a01ca8724e36 1. 烧水举例 用生活中最常见的煮开水举例。出场人物:...

Charles Ren's Tech Blog 1099

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

java io 异步阻塞_IO的阻塞与非阻塞、同步与异步以及Java网络IO交互方式

最近工作中,接触到了Java网络编程方面的东西:Socket、NIO、MongoDB等,也看了tomcat的源码,也加强了线程方面的知识,也使用了MINA这样的框架。感觉获益良多,原本技术上的薄弱环节也在慢慢提高,很多想写的东西,也在慢慢规划整理。想了很久,决定先写写IO中经常被提到的概念——“同步与异步、阻塞与非阻塞”以及在Java网络编程中的简单运用。想达到的目的有两个:1。深入的理解同步与异...

weixin_28753691的博客 156

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

IO模型浅析-阻塞、非阻塞、IO复用、信号驱动、异步IO、同步IO

IO模型浅析-阻塞、非阻塞、IO复用、信号驱动、异步IO、同步IO 原文地址 IO模型浅析-阻塞、非阻塞、IO复用、信号驱动、异步IO、同步IO 正文 IO模型 在《UNIX网络变成卷1:套接字联网API》这本书中,提到了五种I/O模型,分别为:阻塞式I/O、非阻塞式I/O、I/O复用(epoll、select都是一种I/O复用机制),信息驱动式I/O、异步I/O,下面具体的一一介绍。 阻塞式I/...

kaven 1294

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

同步阻塞-同步非阻塞-多路复用-异步阻塞(?)-异步非阻塞

我们将其按照类型进行区分,其实可以分为这几类同步与异步多路复用阻塞与非阻塞可以看到,这些概念单独拿出来,我们可以比较熟练的说出其功能作用,但是连在一起反而不太能反应过来接下来我们利用网络编程中的IO模型来了解这几个模型到底是什么。

2303_79546217的博客 1028

Java网络编程------IO模型的同步/异步/阻塞/非阻塞(1)

IO模型一、BIO模型二、NIO模型 一、BIO模型 二、NIO模型

豢龙先生 337

java nio 原理 非阻塞IO Netty

一、为什么必须去了解NIO首先你需要之后Netty的主要实现手段就是Nio,很多人一直学不明白Netty,根本原因是 除了日常开发中很难能够实践,很大一部分原因是不熟悉NIO,事实上真正熟悉了NIO和它背后的原理之后,去查看Netty的源码就有如神助!我们今天就从最基本的IO、以及NIO学起!二、操作系统是如何定义I/O...

cnzzs的博客 1204

IO模型

前言 说到IO模型,都会牵扯到同步、异步、阻塞、非阻塞这几个词。从词的表面上看,很多人都觉得很容易理解。但是细细一想,却总会发现有点摸不着头脑。自己也曾被这几个词弄的迷迷糊糊的,每次看相关资料弄明白了,然后很快又给搞混了。经历过这么几次之后,发现这东西必须得有所总结提炼才不至于再次混为一谈。尤其是最近看到好几篇讲这个的文章,很多都有谬误,很容易把本来就搞不清楚的人弄的更加迷糊。 最适合IO模型...

一念永恒 261

Java网络编程——基础概念

在学习Java网络编程之前,先来了解一下涉及到的基础知识。

点滴汇聚,智在积累。——Danny 625

Linux IO模型和网络编程模型

目录 Linux IO模型 同步阻塞 同步非阻塞 IO复用 信号驱动 异步非阻塞 网络编程模型 BIO NIO 总结 术语概念描述: IO有内存IO、网络IO和磁盘IO三种,通常我们说的IO指的是后两者。 阻塞和非阻塞,是函数/方法的实现方式,即在数据就绪之前是立刻返回还是等待。 以文件IO为例,一个IO读过程是文件数据从磁盘→内核缓冲区→用户内...

Think88666的博客 284

IO的阻塞与非阻塞、同步与异步以及Java网络IO交互方式

转自:http://www.cnblogs.com/zhuYears/archive/2012/09/28/2690194.html 最近工作中,接触到了Java网络编程方面的东西:Socket、NIO、MongoDB等,也看了tomcat的源码,也加强了线程方面的知识,也使用了MINA这样的框架。感觉获益良多,原本技术上的薄弱环节也在慢慢提高,很多想写的东西,也在慢慢规划整理。无奈最近在筹备婚

颖辉小居 656

java网络编程IO系列之BIO

IO简介 IO即为Input/Output,IO可分为两种磁盘IO和网络IO。顾名思义,磁盘IO指的是从磁盘中进行输入和输出,也就是磁盘数据的读取和存储;而网络IO便指的是网络数据的读取和写入,具体的数据读取方式则是前文提到的JAVA网络编程之Socket。 网络IO的三种方式 java中的网络IO经历了三个阶段: BIO:同步堵 NIO:同步非堵塞 AIO:异步非堵塞 ...

qq_36401386的博客 252

BIO阻塞模型

本章是对Java中传统IO编程模型的解读,在实际工作中可以根据实际情况选择对该模型的应用。扫码关注编程之艺术一起学习、进步。

I_LV_XJ的博客 553

Java基础---IO模型

同步阻塞IO 同步非阻塞IO IO复用模型 信号量驱动IO模型 异步IO

xiaoxishuaidaile的博客 137
上一篇: OpenCV - 最大熵分割
下一篇: Java网络编程 - 非阻塞IO模型
空空的司马
博客等级 码龄12年 233粉丝 117原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

空空的司马

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值