基于.net2.0封装的环形缓冲区组件和高度封装的串口

3 篇文章 0 订阅
3 篇文章 0 订阅

本文主要介绍环形缓冲区组件。

 *组件简介:此组件包含两种读写方法,一种是允许出现碰头的读写算法,
 *一种是不允许出现碰头的读写算法。此算法实现起来整个代码比较简介,
 *相比于不允许出现碰头的读写算法,运行效率要高点,但是允许出现碰头
 *的读写算法运行出现的误码和丢包率的几率要比不允许出现碰头的读写算
 *法要高。此外不允许出现碰头的读写算法运行过程中出现碰头的情况会给
 *出异常信号RingBufStateE = RingBufStateEnum.Abnormal。此异常已经
 *在此组件里进行了异常处理。
 *
 *允许出现碰头的读写算法使用以下读写方法:
 *1、读环形缓冲区数据函数:
 * public bool ReadRingBufDataMeet(ref byte[] readDataToBuf, byte head, byte id)或者
 * public bool ReadRingBufDataMeet(ref byte[] readDataToBuf, byte head)
 *2、写环形缓冲区数据函数:
 * public bool WriteRingBufDataMeet(byte[] writeDataToBuf)
 *不允许出现碰头的读写算法使用以下读写方法:
 *1、读环形缓冲区数据函数:
 * public bool ReadRingBufDataNoMeet(ref byte[] readDataToBuf, byte head, byte id)或者
 * public bool ReadRingBufDataNoMeet(ref byte[] readDataToBuf, byte head)
 *2、写环形缓冲区数据函数:
 * public bool WriteRingBufDataNoMeet(byte[] writeDataToBuf)
 * 
 * 此组件具有以下特点:
 * 1、目前只支持单生产、单消费模式。
 * 2、此组件是一个独立的动态类,使用时只需要创建环形缓冲区对象。
 * 3、此组件是基于.net 2.0封装的动态类,兼容性和稳定性更好。
 * 4、此组件通过虚拟串口实测,波特率9600,自动发送8个数据(有干扰数据),间隔1ms,测试
 * 下来,只有丢掉两个数据包。这种情况是基于环形缓冲区设置的偏大,2000个字节以上,之所以
 * 设置这么大也适合微软的串口控件有关,限于篇幅,此处不赘述。

下面贴出组件源码:

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace TornadoRingBuffer
{
    public class RingBuffer
    {
        #region 构造函数
        public RingBuffer(int ringBufSize)
        {
            _ringBufSize = ringBufSize;
            RingBufSetS.RingBuf = new byte[_ringBufSize];
            RingBufSetS.CanReadSize = 0;
            RingBufSetS.CanWriteSize = _ringBufSize-1;
            RingBufSetS.ReadSite = 0;
            RingBufSetS.WriteSite = 1;
            RingBufStateE = RingBufStateEnum.Good;
        }
        #endregion

        #region 环形缓冲区大小
        private int _ringBufSize;
        /// <summary>
        /// 获取缓冲区大小(只可以获取缓冲区大小,初始化缓冲区后不能再设置缓冲区)
        /// </summary>
        public int RingBufSize
        {
            get
            {
                return _ringBufSize;
            }
        }
        #endregion

        #region 环形缓冲区状态
        public RingBufStateEnum RingBufStateE
        {
            get;
            set;
        }
        #endregion

        #region 环形缓冲区参数设置
        private struct RingBufSetStruct
        {
            public byte[] RingBuf;
            public int CanReadSize;
            public int CanWriteSize;
            public int ReadSite;
            public int WriteSite;
        }
        private RingBufSetStruct RingBufSetS = new RingBufSetStruct();
        #endregion

        #region 读取环形缓冲区中的数据
        #region 计算CanReadSizeMeet字节大小
        /// <summary>
        /// 计算CanReadSizeMeet字节大小
        /// </summary>
        /// <returns>计算成功返回true;计算失败返回false;</returns>
        private bool CalculationCanReadSizeMeet()
        {
            try
            {
                if (RingBufSetS.ReadSite < RingBufSetS.WriteSite)
                {
                    RingBufSetS.CanWriteSize = RingBufSetS.RingBuf.Length - RingBufSetS.WriteSite + RingBufSetS.ReadSite;
                    RingBufSetS.CanReadSize = RingBufSetS.WriteSite - RingBufSetS.ReadSite;
                }
                else if (RingBufSetS.ReadSite > RingBufSetS.WriteSite)
                {
                    RingBufSetS.CanWriteSize = RingBufSetS.ReadSite - RingBufSetS.WriteSite;
                    RingBufSetS.CanReadSize = RingBufSetS.RingBuf.Length - RingBufSetS.ReadSite + RingBufSetS.WriteSite;
                }
                else if (RingBufSetS.ReadSite == RingBufSetS.WriteSite)
                {
                    RingBufSetS.CanWriteSize = RingBufSetS.RingBuf.Length;
                    RingBufSetS.CanReadSize = 0;
                }
                return true;
            }
            catch { return false; }
        }
        #endregion

        #region 读取环形缓冲区中的数据(碰头)
        /// <summary>
        /// 读取缓冲区中的数据
        /// </summary>
        /// <param name="readDataToBuf">存储读取到的一帧数据</param>
        /// <param name="head">一帧数据的头</param>
        /// <param name="id">一帧数据的id号</param>
        /// <returns>成功读取一帧数据返回true;失败读取一帧数据返回false;</returns>
        public bool ReadRingBufDataMeet(ref byte[] readDataToBuf, byte head, byte id)
        {
            try
            {
                if (CalculationCanReadSizeMeet() == false) return false;
                if (readDataToBuf.Length > RingBufSetS.CanReadSize) return false;
                for (; RingBufSetS.CanReadSize > 0; RingBufSetS.CanReadSize--)
                {
                    if (RingBufSetS.ReadSite == RingBufSetS.RingBuf.Length)//读到环形缓冲区的某尾
                    {
                        RingBufSetS.ReadSite = 0;
                    }
                    if (RingBufSetS.RingBuf[RingBufSetS.ReadSite] == head)
                    {
                        int tempReadSite = RingBufSetS.ReadSite + 1;
                        if (tempReadSite == RingBufSetS.RingBuf.Length)//读到环形缓冲区的某尾
                        {
                            tempReadSite = 0;
                        }
                        if (RingBufSetS.RingBuf[tempReadSite] == id)
                        {
                            break;//协议正确
                        }
                    }
                    RingBufSetS.ReadSite++;
                }
                if (readDataToBuf.Length > RingBufSetS.CanReadSize) return false;
                if (RingBufSetS.RingBuf.Length - RingBufSetS.ReadSite >= readDataToBuf.Length)
                {
                    Array.Copy(RingBufSetS.RingBuf, RingBufSetS.ReadSite, readDataToBuf, 0, readDataToBuf.Length);
                    RingBufSetS.ReadSite += readDataToBuf.Length;
                }
                else if (RingBufSetS.RingBuf.Length - RingBufSetS.ReadSite < readDataToBuf.Length)
                {
                    Array.Copy(RingBufSetS.RingBuf, RingBufSetS.ReadSite, readDataToBuf, 0, RingBufSetS.RingBuf.Length - RingBufSetS.ReadSite);
                    Array.Copy(RingBufSetS.RingBuf, 0, readDataToBuf, RingBufSetS.RingBuf.Length - RingBufSetS.ReadSite, readDataToBuf.Length - (RingBufSetS.RingBuf.Length - RingBufSetS.ReadSite));
                    RingBufSetS.ReadSite = readDataToBuf.Length - (RingBufSetS.RingBuf.Length - RingBufSetS.ReadSite);//道理同上
                }
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 读取缓冲区中的数据
        /// </summary>
        /// <param name="readDataToBuf">存储读取到的一帧数据</param>
        /// <param name="head">一帧数据的头</param>
        /// <returns>成功读取一帧数据返回true;失败读取一帧数据返回false;</returns>
        public bool ReadRingBufDataMeet(ref byte[] readDataToBuf, byte head)
        {
            try
            {
                if (CalculationCanReadSizeMeet() == false) return false;
                if (readDataToBuf.Length > RingBufSetS.CanReadSize) return false;
                for (; RingBufSetS.CanReadSize > 0; RingBufSetS.CanReadSize--)
                {
                    if (RingBufSetS.ReadSite == RingBufSetS.RingBuf.Length)//读到环形缓冲区的某尾
                    {
                        RingBufSetS.ReadSite = 0;
                    }
                    if (RingBufSetS.RingBuf[RingBufSetS.ReadSite] == head)
                    {
                        break;//协议正确
                    }
                    RingBufSetS.ReadSite++;
                }
                if (readDataToBuf.Length > RingBufSetS.CanReadSize) return false;
                if (RingBufSetS.RingBuf.Length - RingBufSetS.ReadSite >= readDataToBuf.Length)
                {
                    Array.Copy(RingBufSetS.RingBuf, RingBufSetS.ReadSite, readDataToBuf, 0, readDataToBuf.Length);
                    RingBufSetS.ReadSite += readDataToBuf.Length;
                }
                else if (RingBufSetS.RingBuf.Length - RingBufSetS.ReadSite < readDataToBuf.Length)
                {
                    Array.Copy(RingBufSetS.RingBuf, RingBufSetS.ReadSite, readDataToBuf, 0, RingBufSetS.RingBuf.Length - RingBufSetS.ReadSite);
                    Array.Copy(RingBufSetS.RingBuf, 0, readDataToBuf, RingBufSetS.RingBuf.Length - RingBufSetS.ReadSite, readDataToBuf.Length - (RingBufSetS.RingBuf.Length - RingBufSetS.ReadSite));
                    RingBufSetS.ReadSite = readDataToBuf.Length - (RingBufSetS.RingBuf.Length - RingBufSetS.ReadSite);//道理同上
                }
                return true;
            }
            catch
            {
                return false;
            }
        }
        #endregion

        #region 计算CanReadSizeNoMeet字节大小
        /// <summary>
        /// 计算CanReadSizeNoMeet字节大小
        /// </summary>
        /// <returns>计算成功返回true;计算失败返回false;</returns>
        private bool CalculationCanReadSizeNoMeet()
        {
            try
            {
                if (RingBufSetS.ReadSite < RingBufSetS.WriteSite)
                {
                    RingBufSetS.CanWriteSize = RingBufSetS.RingBuf.Length - RingBufSetS.WriteSite + RingBufSetS.ReadSite;
                    RingBufSetS.CanReadSize = RingBufSetS.WriteSite - RingBufSetS.ReadSite - 1;
                }
                else if (RingBufSetS.ReadSite > RingBufSetS.WriteSite)
                {
                    RingBufSetS.CanWriteSize = RingBufSetS.ReadSite - RingBufSetS.WriteSite;
                    RingBufSetS.CanReadSize = RingBufSetS.RingBuf.Length - RingBufSetS.ReadSite + RingBufSetS.WriteSite - 1;
                }
                else if (RingBufSetS.ReadSite == RingBufSetS.WriteSite)
                {
                    #region 针对异常情况做的异常处理
                    if (RingBufSetS.ReadSite == 0) RingBufSetS.ReadSite = RingBufSetS.RingBuf.Length - 1;
                    else  RingBufSetS.ReadSite--;
                    RingBufSetS.CanWriteSize = RingBufSetS.RingBuf.Length - 1;
                    RingBufSetS.CanReadSize = 0;
                    #endregion
                    //发生了碰头事件
                    RingBufStateE = RingBufStateEnum.Abnormal;
                    return false;
                }
                return true;
            }
            catch{ return false; }
        }
        #endregion

        #region 读取环形缓冲区中的数据(不碰头)
        /// <summary>
        /// 读取缓冲区中的数据
        /// </summary>
        /// <param name="readDataToBuf">存储读取到的一帧数据</param>
        /// <param name="head">一帧数据的头</param>
        /// <param name="id">一帧数据的id号</param>
        /// <returns>成功读取一帧数据返回true;失败读取一帧数据返回false;</returns>
        public bool ReadRingBufDataNoMeet(ref byte[] readDataToBuf, byte head, byte id)
        {
            try
            {
                if (CalculationCanReadSizeNoMeet() == false) return false;
                if (readDataToBuf.Length > RingBufSetS.CanReadSize) return false;
                for (; RingBufSetS.CanReadSize > 0; RingBufSetS.CanReadSize--)
                {
                    RingBufSetS.ReadSite++;
                    if (RingBufSetS.ReadSite == RingBufSetS.RingBuf.Length)//读到环形缓冲区的某尾
                    {
                        RingBufSetS.ReadSite = 0;
                    }
                    if (RingBufSetS.RingBuf[RingBufSetS.ReadSite] == head)
                    {
                        int tempReadSite = RingBufSetS.ReadSite + 1;
                        if (tempReadSite == RingBufSetS.RingBuf.Length)//读到环形缓冲区的某尾
                        {
                            tempReadSite = 0;
                        }
                        if (RingBufSetS.RingBuf[tempReadSite] == id)
                        {
                            if (readDataToBuf.Length > RingBufSetS.CanReadSize)
                            {
                                if (RingBufSetS.ReadSite == 0) RingBufSetS.ReadSite = RingBufSetS.RingBuf.Length - 1;
                                else if (RingBufSetS.ReadSite != 0) RingBufSetS.ReadSite--;
                            }
                            break;//协议正确
                        }
                    }
                }
                if (readDataToBuf.Length > RingBufSetS.CanReadSize) return false;
                if (RingBufSetS.RingBuf.Length - RingBufSetS.ReadSite >= readDataToBuf.Length)
                {
                    Array.Copy(RingBufSetS.RingBuf, RingBufSetS.ReadSite, readDataToBuf, 0, readDataToBuf.Length);
                    RingBufSetS.ReadSite += readDataToBuf.Length-1;
                }
                else if (RingBufSetS.RingBuf.Length - RingBufSetS.ReadSite < readDataToBuf.Length)
                {
                    Array.Copy(RingBufSetS.RingBuf, RingBufSetS.ReadSite, readDataToBuf, 0, RingBufSetS.RingBuf.Length - RingBufSetS.ReadSite);
                    Array.Copy(RingBufSetS.RingBuf, 0, readDataToBuf, RingBufSetS.RingBuf.Length - RingBufSetS.ReadSite, readDataToBuf.Length - (RingBufSetS.RingBuf.Length - RingBufSetS.ReadSite));
                    RingBufSetS.ReadSite = readDataToBuf.Length - (RingBufSetS.RingBuf.Length - RingBufSetS.ReadSite)-1;//道理同上
                }
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 读取缓冲区中的数据
        /// </summary>
        /// <param name="readDataToBuf">存储读取到的一帧数据</param>
        /// <param name="head">一帧数据的头</param>
        /// <returns>成功读取一帧数据返回true;失败读取一帧数据返回false;</returns>
        public bool ReadRingBufDataNoMeet(ref byte[] readDataToBuf, byte head)
        {
            try
            {
                if (CalculationCanReadSizeNoMeet() == false) return false;
                if (readDataToBuf.Length > RingBufSetS.CanReadSize) return false;
                for (; RingBufSetS.CanReadSize > 0; RingBufSetS.CanReadSize--)
                {
                    RingBufSetS.ReadSite++;
                    if (RingBufSetS.ReadSite == RingBufSetS.RingBuf.Length)//读到环形缓冲区的某尾
                    {
                        RingBufSetS.ReadSite = 0;
                    }
                    if (RingBufSetS.RingBuf[RingBufSetS.ReadSite] == head)
                    {
                        if (readDataToBuf.Length > RingBufSetS.CanReadSize)
                        {
                            if (RingBufSetS.ReadSite == 0) RingBufSetS.ReadSite = RingBufSetS.RingBuf.Length - 1;
                            else if (RingBufSetS.ReadSite != 0) RingBufSetS.ReadSite--;
                        }
                        break;//协议正确
                    }
                }
                if (readDataToBuf.Length > RingBufSetS.CanReadSize) return false;
                if (RingBufSetS.RingBuf.Length - RingBufSetS.ReadSite >= readDataToBuf.Length)
                {
                    Array.Copy(RingBufSetS.RingBuf, RingBufSetS.ReadSite, readDataToBuf, 0, readDataToBuf.Length);
                    RingBufSetS.ReadSite += readDataToBuf.Length-1;
                }
                else if (RingBufSetS.RingBuf.Length - RingBufSetS.ReadSite < readDataToBuf.Length)
                {
                    Array.Copy(RingBufSetS.RingBuf, RingBufSetS.ReadSite, readDataToBuf, 0, RingBufSetS.RingBuf.Length - RingBufSetS.ReadSite);
                    Array.Copy(RingBufSetS.RingBuf, 0, readDataToBuf, RingBufSetS.RingBuf.Length - RingBufSetS.ReadSite, readDataToBuf.Length - (RingBufSetS.RingBuf.Length - RingBufSetS.ReadSite));
                    RingBufSetS.ReadSite = readDataToBuf.Length - (RingBufSetS.RingBuf.Length - RingBufSetS.ReadSite)-1;//道理同上
                }
                return true;
            }
            catch
            {
                return false;
            }
        }
        #endregion
        #endregion

        #region 向环形缓冲区中写入数据
        #region 计算CalculationCanWriteSizeMeet字节大小
        /// <summary>
        /// 计算CalculationCanWriteSizeMeet字节大小
        /// </summary>
        /// <returns>计算成功返回true;计算失败返回false;</returns>
        private bool CalculationCanWriteSizeMeet()
        {
            try
            {
                if (RingBufSetS.ReadSite < RingBufSetS.WriteSite)
                {
                    RingBufSetS.CanWriteSize = RingBufSetS.RingBuf.Length - RingBufSetS.WriteSite + RingBufSetS.ReadSite;
                    RingBufSetS.CanReadSize = RingBufSetS.WriteSite - RingBufSetS.ReadSite;
                }
                else if (RingBufSetS.ReadSite > RingBufSetS.WriteSite)
                {
                    RingBufSetS.CanWriteSize = RingBufSetS.ReadSite - RingBufSetS.WriteSite;
                    RingBufSetS.CanReadSize = RingBufSetS.RingBuf.Length - RingBufSetS.ReadSite + RingBufSetS.WriteSite;
                }
                else if (RingBufSetS.ReadSite == RingBufSetS.WriteSite)
                {
                    RingBufSetS.CanWriteSize = RingBufSetS.RingBuf.Length;
                    RingBufSetS.CanReadSize = 0;
                }
                return true;
            }
            catch { return false; }
        }
        #endregion

        #region 向环形缓冲区中写数据(碰头)
        /// <summary>
        /// 向环形缓冲区中写数据(碰头)
        /// </summary>
        /// <param name="writeDataToBuf">要写入的数据</param>
        /// <returns>写入成功返回true;写入失败返回false;</returns>
        public bool WriteRingBufDataMeet(byte[] writeDataToBuf)
        {
            try
            {
                if (CalculationCanWriteSizeMeet() == false) return false;
                if (writeDataToBuf.Length >= RingBufSetS.CanWriteSize) return false;
                if (RingBufSetS.CanWriteSize == RingBufSetS.RingBuf.Length)//读到环形缓冲区的某尾
                {
                    RingBufSetS.CanWriteSize = 0;
                }
                if (RingBufSetS.RingBuf.Length - RingBufSetS.WriteSite >= writeDataToBuf.Length)
                {
                    Array.Copy(writeDataToBuf, 0, RingBufSetS.RingBuf, RingBufSetS.WriteSite, writeDataToBuf.Length);
                    RingBufSetS.WriteSite += writeDataToBuf.Length;
                }
                else if (RingBufSetS.RingBuf.Length - RingBufSetS.WriteSite < writeDataToBuf.Length)
                {
                    Array.Copy(writeDataToBuf, 0, RingBufSetS.RingBuf, RingBufSetS.WriteSite, RingBufSetS.RingBuf.Length - RingBufSetS.WriteSite);
                    Array.Copy(writeDataToBuf, RingBufSetS.RingBuf.Length - RingBufSetS.WriteSite, RingBufSetS.RingBuf, 0, writeDataToBuf.Length - (RingBufSetS.RingBuf.Length - RingBufSetS.WriteSite));
                    RingBufSetS.WriteSite = writeDataToBuf.Length - (RingBufSetS.RingBuf.Length - RingBufSetS.WriteSite);
                }
                return true;
            }
            catch
            {
                return false;
            }
        }
        #endregion

        #region 计算CanWriteSizeNoMeet字节大小
        /// <summary>
        /// 计算CalculationCanWriteSizeNoMeet字节大小
        /// </summary>
        /// <returns>计算成功返回true;计算失败返回false;</returns>
        private bool CalculationCanWriteSizeNoMeet()
        {
            try
            {
                if (RingBufSetS.ReadSite < RingBufSetS.WriteSite)
                {
                    RingBufSetS.CanWriteSize = RingBufSetS.RingBuf.Length - RingBufSetS.WriteSite + RingBufSetS.ReadSite;
                    RingBufSetS.CanReadSize = RingBufSetS.WriteSite - RingBufSetS.ReadSite;
                }
                else if (RingBufSetS.ReadSite > RingBufSetS.WriteSite)
                {
                    RingBufSetS.CanWriteSize = RingBufSetS.ReadSite - RingBufSetS.WriteSite;
                    RingBufSetS.CanReadSize = RingBufSetS.RingBuf.Length - RingBufSetS.ReadSite + RingBufSetS.WriteSite;
                }
                else if (RingBufSetS.ReadSite == RingBufSetS.WriteSite)
                {
                    #region 针对异常情况做的异常处理
                    if (RingBufSetS.ReadSite == 0) RingBufSetS.ReadSite = RingBufSetS.RingBuf.Length - 1;
                    else RingBufSetS.ReadSite--;
                    RingBufSetS.CanWriteSize = RingBufSetS.RingBuf.Length - 1;
                    RingBufSetS.CanReadSize = 0;
                    #endregion
                    //发生了碰头事件
                    RingBufStateE = RingBufStateEnum.Abnormal;
                    return false;
                }
                return true;
            }
            catch { return false; }
        }
        #endregion        

        #region 向环形缓冲区中写数据(不碰头)
        /// <summary>
        /// 向环形缓冲区中写数据(不碰头)
        /// </summary>
        /// <param name="writeDataToBuf">要写入的数据</param>
        /// <returns>写入成功返回true;写入失败返回false;</returns>
        public bool WriteRingBufDataNoMeet(byte[] writeDataToBuf)
        {
            try
            {
                if (CalculationCanWriteSizeNoMeet() == false) return false;
                if (writeDataToBuf.Length >= RingBufSetS.CanWriteSize) return false;
                if (RingBufSetS.CanWriteSize == RingBufSetS.RingBuf.Length)//读到环形缓冲区的某尾
                {
                    RingBufSetS.CanWriteSize = 0;
                }
                if (RingBufSetS.RingBuf.Length - RingBufSetS.WriteSite >= writeDataToBuf.Length)
                {
                    Array.Copy(writeDataToBuf, 0, RingBufSetS.RingBuf, RingBufSetS.WriteSite, writeDataToBuf.Length);
                    RingBufSetS.WriteSite += writeDataToBuf.Length;
                }
                else if (RingBufSetS.RingBuf.Length - RingBufSetS.WriteSite < writeDataToBuf.Length)
                {
                    Array.Copy(writeDataToBuf, 0, RingBufSetS.RingBuf, RingBufSetS.WriteSite, RingBufSetS.RingBuf.Length - RingBufSetS.WriteSite);
                    Array.Copy(writeDataToBuf, RingBufSetS.RingBuf.Length - RingBufSetS.WriteSite, RingBufSetS.RingBuf, 0, writeDataToBuf.Length - (RingBufSetS.RingBuf.Length - RingBufSetS.WriteSite));
                    RingBufSetS.WriteSite = writeDataToBuf.Length - (RingBufSetS.RingBuf.Length - RingBufSetS.WriteSite);
                }
                return true;
            }
            catch
            {
                return false;
            }
        }
        #endregion
        #endregion
    }
}

同时上传了我的工程文件以及对应的Demo测试程序Tornado Project,感兴趣的可以下载使用,使用过程中出现任何异常,欢迎反馈给我,以便我立刻修复问题。


QQ:1343263021
微信:18862121743

寻求单片机和上位机项目合作。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值