.net WCF

Windows Communication Foundation(WCF)是由微软发展的一组数据通信的应用程序开发接口,可以翻译为Windows通讯接口,它是.NET框架的一部分。由 .NET Framework 3.0 开始引入
 WCF合并了Web服务、.net Remoting、消息队列和Enterprise Services的功能并集成在Visual Studio中。
在这里插入图片描述
在这里插入图片描述

WCF 部署到IIS中 自动生成的配置文件如下:
客户端Web.config:

<?xml version="1.0" encoding="utf-8"?>

<!--
  有关如何配置 ASP.NET 应用程序的详细消息,请访问
  http://go.microsoft.com/fwlink/?LinkId=169433
  -->

<configuration>
    <system.web>
        <compilation debug="true" targetFramework="4.0" />
    </system.web>

    <system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="BasicHttpBinding_IUser" closeTimeout="00:01:00"
                    openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
                    allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
                    maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
                    messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
                    useDefaultWebProxy="true">
                    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                        maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                    <security mode="None">
                        <transport clientCredentialType="None" proxyCredentialType="None"
                            realm="" />
                        <message clientCredentialType="UserName" algorithmSuite="Default" />
                    </security>
                </binding>
            </basicHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://localhost/User.svc" binding="basicHttpBinding"
                bindingConfiguration="BasicHttpBinding_IUser" contract="WCFService.IUser"
                name="BasicHttpBinding_IUser" />
        </client>
    </system.serviceModel>
</configuration>

服务端Web.config代码:

<?xml version="1.0" encoding="utf-8"?>
<configuration>

  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- 为避免泄漏元数据信息,请在部署前将以下值设置为 false 并删除上面的元数据终结点 -->
          <serviceMetadata httpGetEnabled="true"/>
          <!-- 要接收故障异常详细信息以进行调试,请将以下值设置为 true。在部署前设置为 false 以避免泄漏异常信息 -->
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
 <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>

</configuration>

由上面的两个配置文件我们发现,客户端system.serviceMode节点有我们刚才讲的endpoint,而服务端为什么没有?这不是和我们刚才讲的有违背吗?那么我们看下面手工修改后[把对看起来很复杂并且对当前的学习无用的配置节删掉]的配置文件的代码(我将服务端和客户端放在一块了):
在这里插入图片描述
  那么第一次的配置文件为什么能执行呢?答案是我们把WCF寄宿在IIS上,而IIS默认监听的就是Http协议[B确定了]并且地址也是相对于IIS上的文件地址[A确定了],合同更不用说了,找到User.svc什么都有了[C确定了],所以在服务端就没有必要显示的写出system.serviceModel,不信你试试,把服务端的配置文件中system.serviceModel节删除,程序一样可以运行!服务器端的endpoint确定了,客户端的endpoint自然要和服务端去对应,所以IDE在生成客户端的配置文件里endpoint写的很详细的,而服务端却没有endpoint。

宿主

在这里插入图片描述
 WCF本身不能够独自运行(每个WCF服务必须宿主在一个Windows进程中)。.net 提供了多种宿主供WCF运行,WCF还是非常灵活的。WCF的宿主可以是 Windows 服务、COM+应用程序、WAS(Windows Activation Services,Windows进程激活服务)或IIS、Windows应用程序,或简单的控制台应用程序及任何.net程序。
 在这里插入图片描述
在这里插入图片描述
控制台应用程序宿主
建立宿主

(1)在解决方案下新建控制台输出项目 WCFHost_Console。

(2)添加 System.ServiceModel.dll 的引用。

(3)添加 WCF 服务类库(WCFLibrary)的项目引用。

(4)创建宿主程序,代码如下:

using System;
using WCFLibrary;
using System.ServiceModel;
using System.ServiceModel.Description;

namespace WCFHost_Console
{
    class Program
    {
        static void Main(string[] args)
        {
            //创建宿主的基地址
            Uri baseAddress = new Uri("http://localhost:8080/User");
            //创建宿主
            using (ServiceHost host = new ServiceHost(typeof(User), baseAddress))
            {
                //向宿主中添加终结点
                host.AddServiceEndpoint(typeof(IUser), new WSHttpBinding(), "");
                //将HttpGetEnabled属性设置为true
                ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
                smb.HttpGetEnabled = true;
                //将行为添加到Behaviors中
                host.Description.Behaviors.Add(smb);
                //打开宿主
                host.Open();
                Console.WriteLine("WCF中的HTTP监听已启动....");
                Console.ReadLine();
                host.Close();
            }
        }
    }
}

在这个示例中我们把Endpoint中的ABC,基地址,Behaviors等都**直接写在了代码里,但实际应用过程中都是去依赖配置文件,**为了对比说明我们下面的例子中会使用配置文件。
因为上面是部署到IIS中,协议就是http 所以binding 就不需要再客户端绑定了。
在这里插入图片描述

Windows应用程序宿主

建立宿主

(1)在解决方案下新建Windows窗体应用程序项目 WCFHost_Form。

(2)添加 System.ServiceModel.dll 的引用。

(3)添加 WCF 服务类库(WCFLibrary)的项目引用。

(4)添加应用程序配置文件App.config。

(5)创建宿主程序MainForm窗体,并修改App.config,代码如下:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="WCFLibrary.User">
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8081/User"/>
          </baseAddresses>
        </host>
        <endpoint address="" binding="wsHttpBinding" contract="WCFLibrary.IUser"></endpoint>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="True"/>
          <serviceDebug includeExceptionDetailInFaults="False"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>


using System;
using WCFLibrary;
using System.ServiceModel;
using System.Windows.Forms;
using System.Configuration;

namespace WCFHost_Form
{
    public partial class MainForm : Form
    {
        ServiceHost host;

        public MainForm()
        {
            InitializeComponent();
        }

        //应用程序加载
        private void MainForm_Load(object sender, EventArgs e)
        {
            host = new ServiceHost(typeof(User));
            //打开宿主
            host.Open();
            this.lblState.Text = "WCF中的HTTP监听已启动....";
        }

        //应用程序关闭
        private void MainForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            host.Close();
        }
    }
}

在这里插入图片描述
在这里插入图片描述

配置也是WCF编程中的主要组成部分。在以往的.net应用程序中,我们会把DBConn和一些动态加载类及变量写在配置文件里。但WCF有所不同。他指定向客户端公开的服务,包括服务的地址、服务用于发送和接收消息的传输和消息编码,以及服务需要的安全类型等。使用配置文件后,我们无需编译即可修改WCF的变化的信息,提高了程序的灵活性。

如果在代码里写了配置,那么配置文件将不起作用。
 Web程序在Web.config中配置,应用程序中在App.config中配置。

**

服务配置的主要部分

**
 在Config中配置服务的结点为:<system.serviceModel></system.serviceModel>,在这个节点中主要有三个平级的部分。如下代码所示:

<?xml version="1.0" encoding="utf-8"?>
 <configuration>
   <system.serviceModel>

     <!--配置服务和终结点开始-->
     <services>
       <service>
         <endpoint></endpoint>
       </service>
     </services>
     <!--配置服务和终结点结束-->

     <!--配置绑定开始-->
     <bindings>
       <netTcpBinding>
         <binding>
         </binding>
       </netTcpBinding>
     </bindings>
     <!--配置绑定结束-->

     <!--配置行为开始-->
     <behaviors>
       <serviceBehaviors>
         <behavior>
         </behavior>
       </serviceBehaviors>
     </behaviors>
     <!--配置行为结束-->

   </system.serviceModel>
 </configuration>

在这里插入图片描述

<?xml version="1.0"?>
<configuration>
  <system.serviceModel>


    <!--服务-->
    <services>
      <!--name:名称空间.类型名    name:指定提供服务协定实现的类型,它是完全限定名称(命名空间和类型名称)--> 
      <!--behaviorConfiguration:behavior的名称,请看behavior配置节的名称-->
      <service name="WCFLibrary.User" behaviorConfiguration="MyBehavior">
        <host>
          <baseAddresses>
            <!-- 每种传输协议的baseAddress,用于跟使用同样传输协议Endpoint定义的相对地址组成完整的地址,
                 每种传输协议只能定义一个baseAddress。HTTP的baseAddress同时是service对外发布服务说明页面的URL -->
            <add baseAddress="http://localhost:8732/Design_Time_Addresses/WCFLibrary/Service/"/>
          </baseAddresses>
        </host>
        <!-- 除非完全限定,否则地址将与上面提供的基址相关,每个服务可以有多个Endpoint -->
        <!-- Address:指定这个Endpoint对外的URI,这个URI可以是个绝对地址,也可以是个相对于baseAddress的
                      相对地址。如果此属性为空,则这个Endpoint的地址就是baseAddress-->
        <!--bindingConfiguration:binding的名称,请看binding配置节的名称-->
        <endpoint address="" binding="wsHttpBinding" contract="WCFLibrary.IUser" bindingConfiguration="myHttpBinding">
          <identity>
            <dns value="localhost"/>
          </identity>
        </endpoint>
        <!-- 此终结点不使用安全绑定,应在部署前确保其安全或将其删除-->
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
      </service>
    </services>


    <!--绑定-->
    <bindings>
      <wsHttpBinding>
        <binding name="myHttpBinding">
          <security mode="None">
            <message clientCredentialType="Windows" />
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>


    <!--行为-->
    <behaviors>
      <serviceBehaviors>
        <behavior name="MyBehavior">
          <!-- httpGetEnabled - bool类型的值,表示是否允许通过HTTP的get方法获取sevice的WSDL元数据 -->
          <serviceMetadata httpGetEnabled="True"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>

  </system.serviceModel>
</configuration>

在这里插入图片描述

完整案例:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

WCF在通信过程中有三种模式:请求与答复、单向、双工通信。以下我们一一介绍。
在这里插入图片描述
请求应答模式这种交换模式是使用最多的一中,它有如下特征:
调用服务方法后需要等待服务的消息返回,即便该方法返回 void 类型
相比Duplex来讲,这种模式强调的是客户端的被动接受,也就是说客户端接受到响应后,消息交换就结束了。
在这种模式下,服务端永远是服务端,客户端就是客户端,职责分明。
它是缺省的消息交换模式,设置OperationContract便可以设置为此种消息交换模式
在这里插入图片描述
单向模式:
存在着如下的特征:
只有客户端发起请求,服务端并不会对请求进行回复
不能包含ref或者out类型的参数
没有返回值,返回类型只能为void
通过设置OperationContract的IsOneWay=True可以将满足要求的方法设置为这种消息交换模式
在这里插入图片描述
双工通讯Duplex具有以下特点:

1它可以在处理完请求之后,通过请求客户端中的回调进行响应操作

2.消息交换过程中,服务端和客户端角色会发生调换

3.服务端处理完请求后,返回给客户端的不是reply,而是callback请求

4.Duplex模式对Bindding有特殊的要求,它要求支持Duplex MEP(Message Exchange Pattern),如WSDualHttpBinding和NetTcpBinding
注意:在WCF预定义绑定类型中,WSDualHttpBinding和NetTcpBinding均提供了对双工通信的支持,但是两者在对双工通信的实现机制上却有本质的区别。WSDualHttpBinding是基于HTTP传输协议的;而HTTP协议本身是基于请求-回复的传输协议,基于HTTP的通道本质上都是单向的WSDualHttpBinding实际上创建了两个通道,一个用于客户端向服务端的通信,而另一个则用于服务端到客户端的通信,从而间接地提供了双工通信的实现。而NetTcpBinding完全基于支持双工通信的TCP协议。
支持回调的绑定有4种:WSDualHttpBinding、NetTcpBinding、NetNamedPipeBinding、NetPeerTcpBinding。我们这里用WSDualHttpBinding为例
在这里插入图片描述

//配置文件中的 binding 指定
<endpoint address=""  binding="wsDualHttpBinding" contract="WCFService_DualPlex.IUser"></endpoint>

//服务端接口
using System.ServiceModel;

namespace WCFService_DualPlex
{
    [ServiceContract(CallbackContract = typeof(IUserCallback))]
    public interface IUser
    {
        [OperationContract]
        string ShowName(string name);
    }

    public interface IUserCallback
    {
        [OperationContract(IsOneWay = true)]
        void PrintSomething(string str);
    }
}

//服务端实现
using System.ServiceModel;

namespace WCFService_DualPlex
{
    public class User : IUser
    {
        IUserCallback callback = null;

        public User()
        {
            //获取调用当前操作的客户端实例的通道。
            callback = OperationContext.Current.GetCallbackChannel<IUserCallback>();
        }

        public string ShowName(string name)
        {
            //在服务器端定义字符串,调用客户端的方法向客户端打印
            string str = "服务器调用客户端...";
            callback.PrintSomething(str);
            //返回服务端方法
            return "WCF服务,显示名称:" + name;
        }
    }
}

//客户端调用
using System;
using System.ServiceModel;
using WCFClient_DualPlex.WCFService_DualPlex;

namespace WCFClient_DualPlex
{
    //实现服务端的回调接口
    public class CallbackHandler : IUserCallback
    {
        public void PrintSomething(string str)
        {
            Console.WriteLine(str);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            InstanceContext instanceContext = new InstanceContext(new CallbackHandler());
            UserClient client = new UserClient(instanceContext);
            Console.WriteLine(DateTime.Now);
            string result = client.ShowName("xxxx");
            Console.WriteLine(result);
            Console.WriteLine(DateTime.Now);
            Console.ReadLine();
        }
    }
}

在这里插入图片描述
在这里插入图片描述
**

操作协定的属性:

**
**加粗样式
**
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

WCF 客户端调用服务几种情况

在这里插入图片描述
在这里插入图片描述
ChannelFactory
一个创建不同类型通道的工厂,客户端使用这些通道将消息发送到不同配置的服务终结点。
在这里插入图片描述
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Service;
using System.ServiceModel;
using System.ServiceModel.Channels;

namespace Client3
{
class Program
{
static void Main(string[] args)
{

        EndpointAddress address = new EndpointAddress("http://localhost:1234/UserInfo");
        WSHttpBinding binding = new WSHttpBinding();
        ChannelFactory<IUserInfo> factory = new ChannelFactory<IUserInfo>(binding,address);
        IUserInfo channel = factory.CreateChannel();


        User[] Users = channel.GetInfo(null);
        Console.WriteLine("{0,-10}{1,-10}{2,-10}{3,-10}", "ID", "Name", "Age", "Nationality");
        for (int i = 0; i < Users.Length; i++)
        {
            Console.WriteLine("{0,-10}{1,-10}{2,-10}{3,-10}",
              Users[i].ID.ToString(),
              Users[i].Name.ToString(),
              Users[i].Age.ToString(),
              Users[i].Nationality.ToString());
        }

        ((IChannel)channel).Close();
        factory.Close();
        Console.Read();
    }
}

}
在这里插入图片描述
WCF客户端----异 步—调用服务
有时我们需要长时间处理应用程序并得到返回结果,但又不想影响程序后面代码部分的执行,这时我们就需要考虑使用异步的方式来调用服务。注意这里的异步是完全针对客户端而言的,与WCF服务契约的方法是否异步无关,也就是在不改变操作契约的情况下,我们可以用同步或者异步的方式调用WCF服务。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
服务引用和生成代理类 异步的客户端均如下代码
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

数据协定概念

在这里插入图片描述
在这里插入图片描述
默认情况下,任何给定的 CLR 命名空间(采用 Clr.Namespace 格式)都会映射到“http://schemas.datacontract.org/2004/07/Clr.Namespace”命名空间。 若要重写此默认值,请对整个模块或程序集应用 ContractNamespaceAttribute 属性。 或者,若要控制每种类型的数据协定命名空间,请设置 DataContractAttribute 的 Namespace 属性。

在这里插入图片描述

在这里插入图片描述
**

数据协定等效性特点概述

**
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

KnownTypeAttribute 类概述

在数据到达接收终结点时,WCF 运行库尝试将数据反序列化为公共语言运行库 (CLR) 类型的实例。通过首先检查传入消息选择为反序列化而实例化的类型,以确定消息内容遵循的数据协定。然后反序列化引擎尝试查找实现与消息内容兼容的数据协定的 CLR 类型。反序列化引擎在此过程中允许的侯选类型集称为反序列化程序的“已知类型”集。
让反序列化引擎了解某个类型的一种方法是使用 KnownTypeAttribute。不能将属性应用于单个数据成员,只能将它应用于整个数据协定类型。将属性应用于可能为类或结构的“外部类型”。在其最基本的用法中,应用属性会将类型指定为“已知类型”。只要反序列化外部类型的对象或通过其成员引用的任何对象,这就会导致已知类型成为已知类型集的一部分。可以将多个 KnownTypeAttribute 属性应用于同一类型。
在这里插入图片描述

IUserInfo.cs代码如下:

Service:类库程序,WCF服务端程序。在服务协定接口IUserInfo.cs中定义数据协定类Person,再定义一个数据协定类User。User派生至Person,继承基类Person的构造方法,定义新的属性成员SayHello。定义操作协定GetInfo和GetInfoEx,两者返回类型都为Person。在UserInfo.cs中实现数据协定,在GetInfo中,我们返回Person对象类型,在GetInfoEx中我们返回派生类User类型。由于在GetInfoEx中我们需要传递派生类User类型,所以要在基类数据协定Person上面加上KnownType(typeof(User))特性标记,这样User就能够服务端进行反序列化,供客户端使用。

using System.ServiceModel;using System.Runtime.Serialization;using System;

namespace Service{   
 [ServiceContract]    
 public interface IUserInfo   
  {       
    [OperationContract]   
    Person GetInfo(int id,string name);
    [OperationContract]       
    Person GetInfoEx(int id, string name);    
  }

 [DataContract]   
 [KnownType(typeof(User))]   
  public class Person   
   {       
      [DataMember]     
       public int ID { get; set; }

      [DataMember]     
      public string Name { get; set; }

  public Person(int id, string name)    
      {           
       this.ID = id;       
       this.Name = name;     
         }   
    }

 [DataContract]   
 public class User:Person   
  {       
   public User(int id, string name): base(id,name){}

  [DataMember]       
   public string SayHello   
       {          
         get { return "Hello:" + Name; }      
         set { throw new NotImplementedException(); }    
          }   
   }
}

UserInfo.cs代码如下:
using System;using System.Collections.Generic;
using System.Linq;using System.Text;
namespace Service{
public class UserInfo:IUserInfo
{
public Person GetInfo(int id, string name)
{
return new Person(id, name);
}
public Person GetInfoEx(int id, string name)
{
return new User(id, name);
}
}

WCF消息协定概述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

异常

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

WCF服务配置编辑器【可视化】
个可视化的配置界面(Microsoft Service Configuration Editor),极大的方便开发者进行服务配置
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
点击文件菜单下的另存为选项保存文件,将配置文件内容复制到先前建好的Host工程下的App.config文件中,编译程序后,就可以运行寄宿服务了,到此我们就完成了一个服务程序配置任务。
在这里插入图片描述
在这里插入图片描述

WAS

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回答: .Net WCF面试题是关于.NET WCFWindows Communication Foundation)的面试题。WCF是一种用于构建分布式应用程序的微软技术。它提供了一种统一的编程模型,使开发人员能够使用不同的传输协议和编码方式来进行通信。WCF面试题可能涉及到WCF的基本概念、架构、服务契约、终结点等方面的内容。在回答这些问题时,可以引用.NET Framework的相关知识,如.NET Framework是微软的一个应用程序开发框架,它包含了许多不同的组件,如ASP.NET、ADO.NETWindows Forms等。此外,还可以引用HttpHandler的概念,它是ASP.NET中用于处理传入Http请求的低级API。List<T>和Dictionary<K,V>是.NET Framework中常用的集合类型,它们有不同的特点和用途。List<T>是一个动态数组,可以按索引访问元素,而Dictionary<K,V>是一个键值对集合,可以通过键来访问值。它们的区别和优势可以根据具体的需求来进行比较和说明。 #### 引用[.reference_title] - *1* *3* [互联网面试——.Net 面试题](https://blog.csdn.net/Cool2Feel/article/details/118000408)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [.NET软件开发工程师面试题(上)](https://blog.csdn.net/m0_56366948/article/details/127974031)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值