Java基础--------网络编程


网络编程

讲网络编程之前,先提一下计算机网络:

  计算机网络,是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,

             在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。


  网络编程就是用来 实现网络互连的不同计算机上运行的程序间可以进行数据交换。

      我的理解:网络编程就是通过编程语言实现计算机间的数据通信。


要学网络编程就要先了解网络模型

  计算机网络之间以何种规则进行通信,就是网络模型研究问题。


网络模型一般是指
           OSI(Open System Interconnection开放系统互连)参考模型
           TCP/IP参考模型

网络参考模型图如下:

                               

网络通信三要素:IP地址、端口号,传输协议

   1)IP地址:InetAddress
         网络中设备的标识,不易记忆,可用主机名 

          它是计算机的唯一标识


   那么,我们如果获取和操作IP地址呢?
     为了方便我们对IP地址的获取和操作,java提供了一个类InetAddress 供我们使用。

    InetAddress类的使用

       它没有构造方法,想到那些问题呢?
       A:成员都是静态的。
      B:肯定有静态方法返回该类的对象。

       要掌握的功能有:
获取本地主机:public String getHostName()
获取任意主机:public static InetAddress getByName(String host)

主机名:public String getHostName()
主机Ip地址:public String getHostAddress()

  我们随便给出一个十进制的数值:
192.168.3.100
   而它在网络中实际上是如下表示的:
11000000 10101000 00000011 01100100
  那为什么我们不是采用下面这种方式配置ip的呢?
如果采用二进制,不方便记忆。
所以,为了方便记忆,把这种表示形式做了改进:"点分十进制"


   IP地址的分类:
 A类 1.0.0.1---127.255.255.254

      (1)10.X.X.X是私有地址(私有地址就是在互联网上不使用,而被用在局域网络中的地址)

      (2)127.X.X.X是保留地址,用做循环测试用的。
 B类128.0.0.1---191.255.255.254172.16.0.0---172.31.255.255是私有地址。169.254.X.X是保留地址。
 C类192.0.0.1---223.255.255.254192.168.X.X是私有地址
 D类224.0.0.1---239.255.255.254
 E类240.0.0.1---247.255.255.254

      IP地址的组成:

IP地址 = 网络号码+主机地址

A类IP地址:第一段号码为网络号码,剩下的三段号码为本地计算机的号码
256*256*256---16777216
B类IP地址:前二段号码为网络号码,剩下的二段号码为本地计算机的号码
256*256---65536
C类IP地址:前三段号码为网络号码,剩下的一段号码为本地计算机的号码
256


查看本机IP:ipconfig
查看网络是否有问题ping+IP地址
ping 127.0.0.1(本机回环地址)


   2)端口号
        用于标识进程的逻辑地址,不同进程的标识  

           通过360可以查看端口号 

        A:每个网络程序都会至少有一个逻辑端口
B:用于标识进程的逻辑地址,不同进程的标识不一样
C:有效端口:0~65535,其中0~1024系统使用或保留端口。

          一般我们用的话建议用1024以上的。
    

   3)传输协议
通讯的规则
常见协议:TCP,UDP


  UDP
     将数据源和目的封装成数据包中,不需要建立连接;(把数据打包,数据有限制,面向无连接)

     每个数据报的大小在限制在64k;因无连接,是不可靠协议;(不可靠)

     不需要建立连接,所以速度快;(速度快)

             举例:QQ群聊,就是群发一些东西,不管你收到收不到
 TCP
     建立连接,形成传输数据的通道;(建立连接通道)

     在连接中进行大数据量传输;数据无限制)

     通过三次握手完成连接,是可靠协议;(面向连接 即三次握手,可靠)

     必须建立连接,效率会稍低;(速度慢)


 那么平时接触到的例子有哪些呢: 蓝牙,QQ单聊,打电话等  只有和对方建立连接之后才能和对方交流。


Socket

     Socket就是为网络编程提供的一种机制;

      包装了有端口和IP地址;通信的两端都有Socket;

      网络通信其实就是Socket间的通信;数据在两个Socket间通过IO传输。


UDP传输

   DatagramSocket与DatagramPacket


传输的步骤为:
     1、建立发送端,接收端。
     2、建立数据包。
     3、调用Socket的发送接收方法。

     4、关闭Socket。 


    发送端与接收端是两个独立的运行程序。


  通过UDP的传输步骤,下面来写一下发送端和接收端的传输步骤:


   发送端思路:

               1:建立udp的socket服务

 
              2:将要发送的数据封装成数据包


             3:通过udp的socket服务,将数据包发送出


             4:关闭资源


接收端的传输思路:

        1:建立udp的socket服务.

2:通过receive方法接收数据

        3:将收到的数据存储到数据包对象中

4:通过数据包对象的功能来完成对接收到数据进行解析.

        5:可以对资源进行关闭


下面通过上面步骤写一个例子。

 需求:发送端向接收端发送一句话:

发送端代码:

package cn.itcast03;


import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;


/*
 * UDP协议发送数据步骤:
 * A:创建发送端Socket服务对象
 * B:创建数据,并把数据打包
 * C:发送数据
 * D:释放资源
 */
public class SendDemo {
<span style="white-space:pre">	</span>public static void main(String[] args) throws IOException {
<span style="white-space:pre">		</span>//创建发送端Socket服务对象
<span style="white-space:pre">		</span>DatagramSocket ds=new DatagramSocket();


<span style="white-space:pre">		</span>//创建数据,并把数据打包
<span style="white-space:pre">		</span>String  str="Hello ,UDP,我来啦~";


<span style="white-space:pre">		</span>// public DatagramPacket(byte[] buf,int length,InetAddress address,int port)
<span style="white-space:pre">		</span>byte[] bys=str.getBytes();
<span style="white-space:pre">		</span>DatagramPacket dp = new DatagramPacket(bys, bys.length,
<span style="white-space:pre">				</span>InetAddress.getByName("192.168.3.100"), 12306);


<span style="white-space:pre">		</span>//发送数据
<span style="white-space:pre">		</span>// public void send(DatagramPacket p)
<span style="white-space:pre">		</span>ds.send(dp);


<span style="white-space:pre">		</span>// 释放资源<span style="white-space:pre">	</span>
<span style="white-space:pre">		</span>ds.close();
<span style="white-space:pre">	</span>}
}


接收端代码:

package cn.itcast03;


import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;


/*
 * UDP协议接收数据步骤:
 * A:创建接收端Socket服务对象
 * B:创建数据包(接收容器)
 * C:调用接收方法
 * D:解析数据包,把数据显示在控制台
 * E:释放资源
 * 注意:
 *  <span style="white-space:pre">	</span>A:端口一定要一致。
 *  <span style="white-space:pre">	</span>B:Exception in thread "main" java.net.BindException: 
 *  <span style="white-space:pre">	</span>  Address already in use: Cannot bind
 *       启用一次服务,只能使用一次,否则会显示该地址已被使用
 */
public class ReceiveDemo {
<span style="white-space:pre">	</span>public static void main(String[] args) throws IOException{
<span style="white-space:pre">		</span>//创建接收端Socket服务对象
<span style="white-space:pre">		</span>DatagramSocket ds=new DatagramSocket(12306);


<span style="white-space:pre">		</span>//创建数据包(接收容器)
<span style="white-space:pre">		</span>// public DatagramPacket(byte[] buf,int length)
<span style="white-space:pre">		</span>byte[] bys=new byte[1024];


<span style="white-space:pre">		</span>DatagramPacket dp=new DatagramPacket(bys, bys.length);


<span style="white-space:pre">		</span>// 调用接收方法
<span style="white-space:pre">		</span>// public void receive(DatagramPacket p)
<span style="white-space:pre">		</span>ds.receive(dp);


<span style="white-space:pre">		</span>// 解析数据包,把数据显示在控制台
<span style="white-space:pre">		</span>// public InetAddress getAddress()


<span style="white-space:pre">		</span>String ip=dp.getAddress().getHostAddress();
<span style="white-space:pre">		</span>String s=new String(dp.getData(), 0,dp.getLength());
<span style="white-space:pre">		</span>System.out.println(ip+"***"+s);


<span style="white-space:pre">		</span>//释放资源
<span style="white-space:pre">		</span>ds.close();


<span style="white-space:pre">	</span>}
}
运行后结果为:

                             

         注意:一定要先编译接收端,然后再编译发送端。(因为UDP不保证数据被收到)



TCP传输


Socket和ServerSocket


  TCP传输的步骤为:
1、建立客户端和服务器端

2、建立连接后,通过Socket中的IO流进行数据的传输

3、关闭socket

         同样,客户端与服务器端是两个独立的应用程序。(客户端可以有多个,而服务器只要一个)


由上面TCP传输的步骤,我们可以写出客户端和服务器端的步骤:


  客户端思路:

1、建立客户端的Socket服务,并明确要连接的服务器。


2、如果连接建立成功,就表明,已经建立了数据传输的通道;


              就可以在该通道通过IO进行数据的读取和写入;


              该通道称为Socket流,Socket流中既有读取流,也有写入流。



3、通过Socket对象的方法,可以获取这两个流


4、通过流的对象可以对数据进行传输


5、如果传输数据完毕,关闭资源


服务器端思路:

     1、建立服务器端的socket服务,需要一个端口


2、服务端没有直接流的操作,而是通过accept方法获取客户端对象,再通过获取到的客户端对象的流和客户端进行通信


3、通过客户端的获取流对象的方法,读取数据或者写入数据


4、如果服务完,需要关闭客户端,然后关闭服务器,
但是,一般会关闭客户端,不会关闭服务器,

                      因为服务端是一直提供服务的


下面通过上面步骤写一个例子。

需求:客户端向服务器端发送一句话:

客户端代码:

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

/*
 * TCP协议客户端的步骤:
 * A:创建客户端的Socket对象
 * B:建立连接
 * C:获取输出流,写数据即可
 * D:释放资源
 *  注意:
 * Exception in thread "main" java.net.ConnectException: Connection refused: connect
 * TCP程序,必须先开服务器端。
 */
public class ClientDemo {
	public static void main(String[] args) throws IOException, IOException {
		//创建客户端的Socket对象
		// public Socket(InetAddress address,int port)
		// Socket s = new Socket(InetAddress.getByName("192.168.3.100"), 10010);
		// Socket(String host, int port)

		Socket s=new Socket("192.168.8.51",10010);

		//获取输出流,写数据即可
		// public OutputStream getOutputStream()
		OutputStream os=s.getOutputStream();
		os.write("hello,TCP,俺来啦~".getBytes());

		//释放资源
		s.close();
	}
}

服务器端代码:

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

/*
 * TCP协议服务器端的步骤:
 * A:创建服务器端Socket对象
 * B:监听连接
 * C:获取输入流,读取数据,并显示
 * D:释放资源
 */
public class ServerDemo {
	public static void main(String[] args) throws IOException {
		//创建服务器端Socket对象
		// ServerSocket(int port)
		ServerSocket ss=new ServerSocket(10010);

		//监听连接
		// public Socket accept()
		Socket s=ss.accept(); //这时候是阻塞的,直到连接上客户端发来的请求后,

		//获取输入流,读取数据,并显示
		// public InputStream getInputStream()
		InputStream is=s.getInputStream();
		byte[] bys=new byte[1024];
		int len=is.read(bys);  //阻塞
		String client=new String(bys,0,len);
		System.out.println(client);

		//释放资源	
		s.close();
	}
}

运行后结果:

                     


在这儿一定要注意:

               运行的时候,先运行服务器端(接受端),再运行客户端(发送端),

               因为服务器先启动,然后在那儿等着客户端的连接请求,即处于阻塞状态,

             这时候启动客户端,然后在那儿找 找 找~~ 找到了对应的IP和端口号,

             就向此服务器发送连接请求, 如果服务器端监听到了客户端的信息,就建立连接啦~~

              否则会连接失败。(这点一直困扰我好长时间,呜呜呜呜~~~)


 下面写几个例子来巩固一下所学的知识:

练习一:实现客户端发送消息,服务器端给出反馈。

客户端代码:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class ClientDemo {
	public static void main(String[] args)throws IOException {
		//创建客户端Socket对象
		Socket s=new Socket("192.168.8.51",12345);

		//获取输出流
		OutputStream os=s.getOutputStream();
		os.write("今天感觉有点累,不过收获的挺大的!".getBytes());

		//获取输入流
		InputStream is=s.getInputStream();
		byte[] bys=new byte[1024];
		int len=is.read(bys);//阻塞
		String client=new String(bys, 0, len);
		System.out.println("client:"+client);

		//释放资源
		s.close();
	}
}

服务器端代码:

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

public class ServerDemo {
	public static void main(String[] args) throws IOException {
		//创建服务器对象Socket对象
		ServerSocket ss=new ServerSocket(12345);

		//监听客户端连接
		Socket s=ss.accept(); //阻塞

		//获取输入流
		InputStream is=s.getInputStream();
		byte[] bys=new byte[1024];
		int len=is.read(bys); //阻塞
		String server=new String(bys, 0, len);
		System.out.println("server:"+server);

		//获取输出流
		OutputStream os=s.getOutputStream();
		os.write("数据已成功接收".getBytes());

		//释放资源
		s.close();
	}
}
运行后结果为:

                                   


                                                                 


练习二:

            需求:数据来源于键盘录入,通过服务器转成大写,然后反馈给客户端显示。


客户端代码:

<span style="font-family: SimSun; font-size: 14px; line-height: 26px;">import java.io.BufferedReader;</span>

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;

/*
 * 需求:数据来源于键盘录入,通过服务器转成大写,然后反馈给客户端显示。
 */
public class ClientDemo {
	public static void main(String[] args)throws IOException {
		//创建客户端Socket对象
		Socket s=new Socket("192.168.8.51",22222);

		//封装键盘录入
		BufferedReader br=new BufferedReader(new InputStreamReader(System.in));

		//获取输出流
		BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

		String line=null;
		while((line=br.readLine())!=null){
			if("over".equals(line)){
				break;
			}
			bw.write(line);
			bw.newLine();
			bw.flush();

			//获取服务器的反馈
			BufferedReader brServer=new BufferedReader(new InputStreamReader(s.getInputStream()));
			String server=brServer.readLine();
			System.out.println("serer:"+server);
		}
		br.close();
		s.close();
	}
}


服务器端代码:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerDemo {
	public static void main(String[] args) throws IOException {
		//创建服务器Socket对象
		ServerSocket ss=new ServerSocket(22222);

		//监听客户端
		Socket s=ss.accept();

		//获取输入流
		BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));
		BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

		String line=null;
		while((line=br.readLine())!=null){
			bw.write(line.toUpperCase());
			bw.newLine();
			bw.flush();	
		}
		s.close();
	}
}

        运行后结果为:

                         

上面的练习其实还可以用PrintWriter来改进:

客户端:

/*
 * 需求:数据来源于键盘录入,通过服务器转成大写,然后反馈给客户端显示。
 */
public class ClientDemo {
	public static void main(String[] args)throws IOException {
		//创建客户端Socket对象
		Socket s=new Socket("192.168.8.51",22222);

		//封装键盘录入
		BufferedReader br=new BufferedReader(new InputStreamReader(System.in));

		//获取输出流
		//BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
         PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
		String line=null;
		while((line=br.readLine())!=null){
			if("over".equals(line)){
				break;
			}
//			bw.write(line);
//			bw.newLine();
//			bw.flush();
			pw.println(line);

			//获取服务器的反馈
			BufferedReader brServer=new BufferedReader(new InputStreamReader(s.getInputStream()));
			String server=brServer.readLine();
			System.out.println("serer:"+server);
		}
		br.close();
		s.close();
	}
}

服务器端:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerDemo {
	public static void main(String[] args) throws IOException {
		//创建服务器Socket对象
		ServerSocket ss=new ServerSocket(22222);

		//监听客户端
		Socket s=ss.accept();

		//获取输入流
		BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));
		//BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
    PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
		String line=null;
		while((line=br.readLine())!=null){
//			bw.write(line.toUpperCase());
//			bw.newLine();
//			bw.flush();
			pw.println(line.toUpperCase());
		}
		s.close();
	}
}

运行后结果和上面是一样的,但是代码简化了不少。


练习三:

需求:键盘录入数据,并写入到文本文件。

   写一下刚开始遇到的问题:
       写数据,数据进去了,但是反馈没有成功。
           原因:就是因为服务器不知道你已经写完了。
        
       怎么解决呢?告诉服务器我写完了。
       怎么告诉:
        A:自定义接收标记     假如有一个数据叫“拜拜” ,就会被强制终止
        B:Socket提供了一个方法,来通知我接收了。
             public void shutdownOutput()  

客户端代码:

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

/*
 * 需求:数据来源于键盘录入,并写入文本文件中 
 */
public class ClientDemo {
	public static void main(String[] args)throws IOException {
		//创建客户端Socket对象
		Socket s=new Socket("192.168.8.51",22222);

		//封装键盘录入
		BufferedReader br=new BufferedReader(new InputStreamReader(System.in));

		//获取输出流
		PrintWriter pw=new PrintWriter(s.getOutputStream(),true);

		String line=null;
		while((line=br.readLine())!=null){
			if("over".equals(line)){
				break;
			}
			pw.println(line);
		}

		s.shutdownOutput();
		//获取服务器的反馈
		BufferedReader brServer=new BufferedReader(new InputStreamReader(s.getInputStream()));
		String server=brServer.readLine();
		System.out.println("serer:"+server);

		br.close();
		s.close();
	}
}
服务器端代码:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerDemo {
	public static void main(String[] args) throws IOException {
		//创建服务器Socket对象
		ServerSocket ss=new ServerSocket(22222);

		//监听客户端
		Socket s=ss.accept();

		//获取输入流
		BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));
		PrintWriter out=new PrintWriter(new FileWriter("out.txt"),true);
		String line=null;
		while((line=br.readLine())!=null){

			out.println(line);
		}
		PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
		pw.println("数据成功接收");
		s.close();
	}
}
     运行后结果为:

                  

练习四:

 需求:客户端向服务端发送用户名请求登陆,服务端通过验证,返回“欢迎光临”,未通过“用户不存在”

服务器端代码:

/*
 * 客户端向服务端发送用户名请求登陆,服务端通过验证,返回“欢迎光临”,未通过“用户不存在”  
 */
public class ServerDemo {
	public static void main(String[] args) throws IOException {
		//创建服务器Socket对象
		ServerSocket ss=new ServerSocket(11111);

		//监听客户端对象
		Socket s=ss.accept();

		//获取输入流
		BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));

		PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
		String line=br.readLine();
		if("admin".equals(line)){
			pw.println("欢迎光临!");
		}else{
			pw.println("用户不存在!");
		}
		//释放资源
		s.close();
		ss.close();
	}
}
客户端代码:

public class ClientDemo {
	public static void main(String[] args)throws IOException {
		//创建客户端Socket对象
		Socket s=new Socket("192.168.8.51",11111);

		//获取输出流
		PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
		pw.println("hello");

		//获取输入流
		BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));

		String line=br.readLine();

		System.out.println("line:"+line);

		//释放资源
		s.close();
	}
}
运行后结果为:

                       
练习五:

需求: 客户端向服务端上传一个文本文件
服务器端代码:

/*
 * 程序可以解决一部分效率问题,但是一般都是小的改进。
 * 要想大的改进:硬件,网络带宽。
 */
public class ServerDemo {
	public static void main(String[] args) throws IOException {
		// 创建服务器Socket对象
		ServerSocket ss = new ServerSocket(12345);

		// 监听客户端对象
		Socket s = ss.accept();

		// 获取输入流
		BufferedReader br = new BufferedReader(new InputStreamReader(
				s.getInputStream()));

		// long time = System.currentTimeMillis();
		// 封装输出流
		// PrintWriter pw = new PrintWriter(new FileWriter(time+"_Copy.java"),
		// true);
		PrintWriter pw = new PrintWriter(new FileWriter("Copy.java"), true);
		String line = null;
		while ((line = br.readLine()) != null) {
			pw.println(line);
		}

		// 给出反馈,获取输出流
		PrintWriter out = new PrintWriter(s.getOutputStream(), true);
		out.println("文件上传成功");

		// 释放资源
		pw.close();
		s.close();
		ss.close();
	}
}
客户端代码:

/*
 * 客户端向服务端上传一个文本文件
 */
public class ClientDemo {
	public static void main(String[] args) throws IOException {
		// 创建客户端对象
		Socket s = new Socket("192.168.8.51", 12345);

		// 封装输入流
		BufferedReader br = new BufferedReader(new FileReader("ClientDemo.java"));
		// 获取输出流
		PrintWriter pw = new PrintWriter(s.getOutputStream(), true);
		String line = null;
		while ((line = br.readLine()) != null) {
			pw.println(line);
		}
		s.shutdownOutput();

		// 获取输入流
		BufferedReader brServer = new BufferedReader(new InputStreamReader(
				s.getInputStream()));
		String server = brServer.readLine();
		System.out.println("server反馈信息是:" + server);
	
		//释放资源
		br.close();
		s.close();
	}
}
      运行后结果:

                   

         通过这几个练习,感觉现在能熟练的运用网络编程方面的知识,自己理解得更透了一些。

        加油吧,博客马上就收尾啦~~



网络编程的知识点儿就先总结到这儿啦,有些理解的不对的地方,真诚地希望大家帮我指正出来,谢谢!!!




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值