网络编程.

网络编程

概念

网页编程 : 关注上层应用

网络编程 : 关注底层数据的传输

IP( Internet Protocol )地址 : 定位网络中的节点…(电脑,手机…)

端口号 : 区分软件

URL : 同一资源定位符

传输层协议 : (标准,合同,规范)

​ UDP : 邮寄包裹|写信 不安全,协议简单,开销小,效率高 大小限制一般不超过60k

​ ******TCP : 打电话 基于连接 安全,效率低 没有大小限制

端口 : 区分软件

2个字节 0~65535表示端口号

同一协议下端口号不能冲突

8000以内称为预留端口号,建议自己设置端口号为8000以上

常见的端口号 : 80 : http 8080 : tomcat 3306 : mysql 1521 : oracle

InetSocketAddress :此类实现IP套接字地址(IP地址+端口号)

		//InetSocketAddress(String hostname, int port) 根据主机名和端口号创建套接字地址。
        InetSocketAddress in1 = new InetSocketAddress("localhost",9999);
        System.out.println(in1);
        System.out.println(in1.getHostName());
        System.out.println(in1.getPort());
        //InetSocketAddress(InetAddress addr, int port) 根据IP地址和端口号创建套接字地址。

url : 统一资源定位符,互联网中资源的指针

互联网的三大基石 : http html url

url组成 : 协议 : http 域名 : www.baidu.com 端口 : 80

​ 资源 : index.html?username=zhangsan&password=1234#a

​ 例:http://www.baidu.com:80/index.html?username=zhangsan&password=1234#a*/

	    //URL(String spec)
        URL url = new URL("http://www.baidu.com:80/index.html?username=zhangsan&password=1234#a");
        System.out.println(url);
        System.out.println(url.getProtocol());
        System.out.println(url.getHost());
        System.out.println(url.getPort());
        System.out.println(url.getPath());
        System.out.println(url.getFile());
        System.out.println(url.getQuery());
        System.out.println(url.getRef());

接口

为了使两个结点之间能进行对话,必须在它们之间建立通信工具(即接口),使彼此之间能进行信息交换。
接口包括两部分:
硬件装置: 实现结点之间的信息传送
软件装置: 规定双方进行通信的约定协议

协议

计算机网络中实现通信必须有一些约定即通信协议,对速率、传输代码、代码结构、传输控制步骤、出错控制等制定标准。显然,每一行数据都有“姓名”、“基本工资”等“列”,也都有标准的“晨会动作”。网络通信协议由三个要素组成 : 语义 、 语法 、 时序

语义表示要做什么,语法表示要怎么做,时序表示做的顺序!

网络分层

由于结点之间联系很复杂,在制定协议时,把复杂成份分解成 一些简单的成份,再将它们复合起来。最常用的复合方式是层次方式,即同层间可以通信、上一层可以调用下一层,而与再下一层不发生关系。

通信协议分层

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

封装

封装:从上往下,加入相关层的协议控制信息

在这里插入图片描述

拆封

拆封:获取数据,从下往上,去除相关层的协议的控制信息

在这里插入图片描述

IP

( IP) 是 Internet Protocol 的外语缩写, 网络之间互连的协议也就是为计算机网络相互连接进行通信而设计的协议。 在因特网中,它是能使连接到网上的所有计算机网络实现相互通信的一套规则,规定了计算机在因特网上进行通信时应当遵守的规则。任何厂家生产的计算机系统,只要遵守 IP 协议就可以与因特网互连互通。

定位网络中的节点…(电脑,手机…)

​ IPV4 4个字节32位 IPV6128位

​ 特殊的IP :

​ 192.168.0.0~192.168.255.255 非注册IP,供组织内部使用的IP 127.0.0.1 本地IP

​ localhost : 本地

端口:区分数据流向的软件 0-65535 不要使用 1024 以下的端口 ,每一个协议拥有自己的端口,在同一个协议下端口不能重复 FTP:21 HTTP:80

InetAddress:封装计算机的ip地址和DNS(没有端口信息!)
这个类没有构造函数。如果要得到对象,只能通过静态方法:getLocalHost , getByName ,getAllByName , getAddress , getHostNameg

        //static InetAddress getLocalHost() 返回本地主机的地址。
        InetAddress ip1 = InetAddress.getLocalHost();
        System.out.println(ip1);  
        System.out.println(ip1.getHostName());
        System.out.println(ip1.getHostAddress());
        //static InetAddress getByName(String host) 根据主机名称确定主机的IP地址。
        InetAddress ip2 = InetAddress.getByName("www.yjxxt.com");
        System.out.println(ip2);
        System.out.println(ip2.getHostName());
        System.out.println(ip2.getHostAddress());

InetSocketAddress:包含IP和端口,常用于SOCKET通信。

端口(port)和url

端口

端口是虚拟的概念,并不是说在主机上真的有若干个端口。通过端口,可以在一个主机 上运行多个网络应用程序。可以类比为:IP 相当于公司,端口相当于公司各部门,URL相当于各部门的人员,包含端口,用于 socket 通信的

//1) 、获取对象
InetSocketAddress(String hostname, int port)
InetSocketAddress(InetAddress addr, int port)
//2) 、方法
getAddress() //返回 InetAddress 对象
getPort() //返回端口
getHostName() //返回域名
InetSocketAddress socketAddress = new InetSocketAddress("127.0.0.1",8080);
InetSocketAddress socketAddress2 = new InetSocketAddress("localhost",9000);
System.out.println(socketAddress.getHostName());
System.out.println(socketAddress2.getAddress());

url

URL全称是Uniform Resource Location,也就是统一资源位置。实际上,URL就是一种特殊的URI,它除了标识一个资源,还会为资源提供一个特定的网络位置,客户端可以通过它来获取URL对应的资源。
URL所表示的网络资源位置通常包括用于访问服务器的协议(如http、ftp等)、服务器的主机名或者IP地址、以及资源文件在该服务器上的路径。典型的URL例如http://localhost:port/myProject/index.html

URL的语法表示形式为:

protocol://userInfo@host:port/path?query#fragment
协议://用户信息@主机名:端口/路径?查询#锚点

IP地址唯一标识了Internet上的计算机,而URL则标识了这些计算机上的资源。类 URL 代表一个统一资源定位符,它是指向互联网“资源”的指针。资源可以是简单的文件或目录,也可以是对更为复杂的对象的引用,例如对数据库或搜索引擎的查询。
为了方便程序员编程,JDK中提供了URL类,该类的全名是 java.net.URL ,有了这样一个类,就可以使用它的各种方法来对URL对象进行分割、合并等处理。
java.net.URL 类(后面直接叫URL)是JDK对URL的统一抽象,它是一个final修饰的类,也就是不允许派生子类。

网络爬虫原理

public static void main(String[] args) throws IOException {
	URL url=new URL("http://www.baidu.com");//主页 默认资源
	//获取资源 网络流 可能会出现乱码:1、字节数不够 2、编码解码格式不统一
	/*InputStream is=url.openStream();
	byte[]flush=new byte[1024];
	int len=0;
	while(-1!=(len=is.read(flush))){
		System.out.println(new String(flush,0,len));
	}
	is.close();*/
	BufferedReader br=new BufferedReader(new InputStreamReader(url.openStream(),"utf-8"));
	BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("baidu2.html"),"utf-8"));
	String msg=null;
	while((msg=br.readLine())!=null){
		//将网络内容下载到本地。然后进行数据分析,建立索引,这也是搜索引擎的第一步。
		bw.append(msg);
		bw.newLine();//新增方法 插入换行符
	}
	bw.flush();//强制刷出
	bw.close();
	br.close();
}

传输层协议

TCP:TCP(transfer control protocol) 打电话 面向连接、安全、可靠,效率低
UDP:UDP(UserDatagramProtocol ) 发送短信 非面向连接、不安全、数据可能丢失 、效率高

UDP

UserDatagramProtocol,一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务。其特点为:非面向连接;传输不可靠;数据可能丢失。

DatagramSocket:用于发送或接收数据报的数据报包:

当服务器要向客户端发送数据时,需要在服务器端产生一个DatagramSocket对象,在客户端产生一个DatagramSocket对象。服务器端的DatagramSocket将DatagramPacket发送到网络上,然后被客户端的DatagramSocket接收。
DatagramSocket有两种构造函数。一种是无需任何参数的,常用于客户端。另一种需要指定端口,常用于服务器。常用方法: send , receive , close

DatagramPacket:数据容器(封包)的作用:

常用方法:构造函数、getAddrress(获取发送或接收方计算机的Ip地址)、getData(获取发送或接收的数据),setData(设置发送的数据)

UDP发送端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
/*
    Socket : 套接字
        传输层为应用层开辟的一个小口子
    面向Socket编程,不同的传输层协议对Socket的实现不同

    UDP协议实现 :
        两端平等 : 发送端  接口端
        UDP协议下数据传输基于字节数组
        DatagramSocket
            DatagramSocket(int port) 构造一个数据报套接字并将其绑定到本地主机上的指定端口。
            void receive(DatagramPacket p) 从此套接字接收数据报包。
            void send(DatagramPacket p) 从此套接字发送数据报包。

        DatagramPacket : 该类表示数据报包。

    UDP协议下基本流程 : 发送端
        1.定义我是发送端
        2.准备数据
        3.打包
        4.发送
        5.关闭
 */
public class Class001_Send {
    public static void main(String[] args) throws IOException {
        System.out.println("----------------发送端-----------------");
        //1.定义我是发送端
        DatagramSocket sendSocket = new DatagramSocket(9999);
        //2.准备数据
        byte[] arr = "哈啰".getBytes();
        //3.打包
        DatagramPacket packet = new DatagramPacket(arr,0,arr.length,new InetSocketAddress("127.0.0.1",8888));
        //4.发送
        sendSocket.send(packet);
        //5.关闭
        sendSocket.close();
    }
}
UDP接收端
import javax.xml.crypto.Data;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

/*
    UDP协议下基本流程 : 接收端
        1.定义我是接收端
        2.准备包裹,用来接收数据
        3.接收数据
        4.处理数据
        5.关闭
 */
public class Class002_Receive {
    public static void main(String[] args) throws IOException {
        System.out.println("--------------------接收端------------------");
        //1.定义我是接收端
        DatagramSocket receiveSocket = new DatagramSocket(8888);
        //2.准备包裹,用来接收数据
        byte[] arr = new byte[1024];
        DatagramPacket packet = new DatagramPacket(arr,0,arr.length);
        //3.接收数据
        receiveSocket.receive(packet);
        //4.处理数据
        //byte[] getData() 返回数据缓冲区。
        byte[] newArr = packet.getData();
        //int getLength() 返回要发送的数据的长度或接收的数据的长度。
        int len = packet.getLength();
        System.out.println(new String(newArr,0,len));
        //5.关闭
        receiveSocket.close();
    }
}

TCP

transfer control protocol,一种面向连接(连接导向)的、可靠的、基于字节流的传输层(Transport layer)通信协议的点到点的通信 。TCP 三次握手(Three-way Handshake)和四次挥手,类似于拨打电话。

特点:

基于 tcp 协议,建立稳定连接的点对点的通信;实时、快速、安全性高、占用系统资源多、效率低;“请求响应”模式:

  1. 客户端:在网络通讯中,第一次主动发起通讯的程序被称作客户端(Client)程序
  2. 服务器:第一次通讯中等待连接的程序被称作服务器端(Server)程序

• Socket:发送 TCP 消息
• ServerSocket:创建服务器

套接字是一种进程间的数据交换机制。这些进程既可以在同一机器上,也可以在通过网络连接的不同机器上。换句话说,套接字起到通信端点的作用。单个套接字是一个端点,而一对套接字则构成一个双向通信信道,使非关联进程可以在本地或通过网络进行数据交换。一旦建立套接字连接,数据即可在相同或不同的系统中双向或单向发送,直到其中一个端点关闭连接。
套接字与主机地址和端口地址相关联。主机地址就是客户端或服务器程序所在的主机的ip地址。端口地址是指客户端或服务器程序使用的主机的通信端口。
实际上,套接字只是计算机上已编号的端口。如果发送方和接收方计算机确定好端口,他们就可以通信了

TCP客户端
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

/*
    TCP : 基于连接的,类似打电话, 安全,效率低  没有大小限制  基础3次握手建立连接  几次挥手断开连接
         协议下两端是不平等的 ,客户端  服务器端
         tcp协议下数据传输基于IO流

    TCP协议下实现基本流程 : 客户端
        1.定义我是客户端 Socket该类实现客户端套接字(也称为“套接字”)。 套接字是两台机器之间通信的端点。
            Socket(String host, int port) 参数ip+端口 指的是服务器的IP+端口
        2.准备数据
        3.获取输出流,向服务器发送数据
        4.刷出
        5.关闭
 */
public class Class001_Client {
    public static void main(String[] args) throws IOException {
        System.out.println("--------------我是客户端--------------");
        //1.定义我是客户端
        Socket client = new Socket("localhost",8989);
        System.out.println("连接建立起来了....");
        //2.准备数据
        String msg = "good good study,day day up!!!";
        //3.获取输出流,向服务器发送数据
        DataOutputStream os = new DataOutputStream(client.getOutputStream());
        os.writeUTF(msg);
        //4.刷出
        os.flush();
        //5.关闭
        os.close();
        client.close();
    }
}
TCP服务器
import java.io.DataInputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/*
    TCP协议下实现基本流程 : 服务器
        1.定义我是服务器 ServerSocket该类实现服务器套接字。
        2.阻塞式监听
        3.获取输入流,读取客户端发送的数据
        4.处理数据
        5.关闭
 */
public class Class002_Server {
    public static void main(String[] args) throws IOException {
        System.out.println("--------------我是服务器--------------");
        //1.定义我是服务器 ServerSocket
        ServerSocket server = new ServerSocket(8989);
        //2.阻塞式监听 Socket accept() 侦听对此套接字的连接并接受它。
        Socket client = server.accept();
        System.out.println("与一个客户端建立连接了.............");
        //3.获取输入流,读取客户端发送的数据
        DataInputStream is = new DataInputStream(client.getInputStream());
        String msg = is.readUTF();
        //4.处理数据
        System.out.println(msg);
        //5.关闭
        is.close();
        client.close();
        server.close();
    }
}

单向登录

客户端
/*

    TCP协议下实现单向登录流程 : 客户端
        1.定义我是客户端
        2.键盘输入接收用户输入的登录信息 username=zhangsan&password=123
        3.获取输出流,向服务器发送数据
        4.刷出
        5.关闭
 */
public class Class003_LoginClient {
    public static void main(String[] args) throws IOException {
        System.out.println("--------------我是客户端--------------");
        //1.定义我是客户端
        Socket client = new Socket("localhost",8989);
        System.out.println("连接建立起来了....");
        //2.准备数据
        BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("请输入用户名:");
        String username = bf.readLine();
        System.out.println("请输入密码:");
        String password = bf.readLine();
        //3.获取输出流,向服务器发送数据
        DataOutputStream os = new DataOutputStream(client.getOutputStream());
        os.writeUTF("username="+username+"&password="+password);
        //4.刷出
        os.flush();
        //5.关闭
        os.close();
        bf.close();
        client.close();
    }
}
服务器
/*
    TCP协议下实现单向登录流程 : 服务端
        1.定义我是服务器
        2.阻塞式监听
        3.获取输入流,读取客户端发送的数据
        4.处理数据
            校验用户输入的登录信息是否正确(admin 1234)
        5.关闭
 */
public class Class004_LoginServer {
    public static void main(String[] args) throws IOException {
        System.out.println("--------------我是服务器--------------");
        //1.定义我是服务器 ServerSocket
        ServerSocket server = new ServerSocket(8989);
        //2.阻塞式监听 Socket accept() 侦听对此套接字的连接并接受它。
        Socket client = server.accept();
        System.out.println("与一个客户端建立连接了.............");
        //3.获取输入流,读取客户端发送的数据
        DataInputStream is = new DataInputStream(client.getInputStream());
        String msg = is.readUTF();
        //4.处理数据
        //1)校验方式
        /*if("username=admin&password=1234".equals(msg)){
            System.out.println("登录成功");
        }else{
            System.out.println("用户名或密码错误");
        }*/
        //2)校验方式
        String uname=null;
        String upwd=null;
        String[] arr = msg.split("&");
        for(String str:arr){
            String[] arr2 = str.split("=");
            if("username".equals(arr2[0])){
                uname = arr2[1];
            }else if("password".equals(arr2[0])){
                upwd = arr2[1];
            }
        }
        if("admin".equals(uname) && "1234".equals(upwd)){
            System.out.println("登录成功");
        }else{
            System.out.println("用户名或密码错误");
        }
        //5.关闭
        is.close();
        client.close();
        server.close();
    }
}

双向登录

客户端
/*

    TCP协议下实现双向登录流程 : 客户端
        1.定义我是客户端
        2.键盘输入接收用户输入的登录信息 username=zhangsan&password=123
        3.获取输出流,向服务器发送数据
        4.刷出
        5.获取输入流,读取服务器响应的结果
        6.处理结果
        7.关闭
 */
public class Class005_LoginTwoWayClient {
    public static void main(String[] args) throws IOException {
        System.out.println("--------------我是客户端--------------");
        //1.定义我是客户端
        Socket client = new Socket("localhost",8989);
        System.out.println("连接建立起来了....");
        //2.准备数据
        BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("请输入用户名:");
        String username = bf.readLine();
        System.out.println("请输入密码:");
        String password = bf.readLine();
        //3.获取输出流,向服务器发送数据
        DataOutputStream os = new DataOutputStream(client.getOutputStream());
        os.writeUTF("username="+username+"&password="+password);
        //4.刷出
        os.flush();
        //5.获取输入流,读取服务器响应的结果
        DataInputStream is = new DataInputStream(client.getInputStream());
        String result = is.readUTF();
        //6.处理结果
        System.out.println(result);
        //7.关闭
        is.close();
        os.close();
        bf.close();
        client.close();
    }
}
服务器
/*
    TCP协议下实现双向登录流程 : 服务端
        1.定义我是服务器
        2.阻塞式监听
        3.获取输入流,读取客户端发送的数据
        4.处理数据
            校验用户输入的登录信息是否正确(admin 1234)
        5.获取输出流,将结果响应给客户端
        6.刷出
        7.关闭
 */
public class Class006_LoginTwoWayServer {
    public static void main(String[] args) throws IOException {
        System.out.println("--------------我是服务器--------------");
        //1.定义我是服务器 ServerSocket
        ServerSocket server = new ServerSocket(8989);
        //2.阻塞式监听
        Socket client = server.accept();
        System.out.println("与一个客户端建立连接了.............");
        //3.获取输入流,读取客户端发送的数据
        DataInputStream is = new DataInputStream(client.getInputStream());
        String msg = is.readUTF();
        //4.处理数据
        //1)校验方式
        /*if("username=admin&password=1234".equals(msg)){
            System.out.println("登录成功");
        }else{
            System.out.println("用户名或密码错误");
        }*/
        //2)校验方式
        String uname=null;
        String upwd=null;
        String[] arr = msg.split("&");
        for(String str:arr){
            String[] arr2 = str.split("=");
            if("username".equals(arr2[0])){
                uname = arr2[1];
            }else if("password".equals(arr2[0])){
                upwd = arr2[1];
            }
        }
        //5.获取输出流,将结果响应给客户端
        DataOutputStream os = new DataOutputStream(client.getOutputStream());
        if("admin".equals(uname) && "1234".equals(upwd)){
            os.writeUTF("登录成功");
        }else{
            os.writeUTF("用户名或密码错误");
        }
        //6.刷出
        os.flush();
        //7.关闭
        os.close();
        is.close();
        client.close();
        server.close();
    }
}

多用户登录

循环实现
/*
    TCP协议下实现多用户登录流程 : 服务端
        服务器端能够实现多多个客户端做响应
        重复执行某段代码可以使用循环,但是多个客户端只能排队登录
 */
public class Class007_LoginMulServer {
    public static void main(String[] args) throws IOException {
        System.out.println("--------------我是服务器--------------");
        //1.定义我是服务器 ServerSocket
        ServerSocket server = new ServerSocket(8989);
        //2.阻塞式监听
        boolean flag = true;
        while(flag){
            Socket client = server.accept();
            System.out.println("与一个客户端建立连接了.............");
            //3.获取输入流,读取客户端发送的数据
            DataInputStream is = new DataInputStream(client.getInputStream());
            String msg = is.readUTF();
            //4.处理数据
            String uname=null;
            String upwd=null;
            String[] arr = msg.split("&");
            for(String str:arr){
                String[] arr2 = str.split("=");
                if("username".equals(arr2[0])){
                    uname = arr2[1];
                }else if("password".equals(arr2[0])){
                    upwd = arr2[1];
                }
            }
            //5.获取输出流,将结果响应给客户端
            DataOutputStream os = new DataOutputStream(client.getOutputStream());
            if("admin".equals(uname) && "1234".equals(upwd)){
                os.writeUTF("登录成功");
            }else{
                os.writeUTF("用户名或密码错误");
            }
            //6.刷出
            os.flush();
            //7.关闭
            os.close();
            is.close();
            client.close();
        }
        server.close();
    }
}
多线程实现
/*
    TCP协议下实现多用户登录流程 : 服务端
        利用多线程实现,监听到一个客户端就开启一个线程提供服务
 */
public class Class008_LoginMulServer {
    public static void main(String[] args) throws IOException {
        System.out.println("--------------我是服务器--------------");
        //1.定义我是服务器 ServerSocket
        ServerSocket server = new ServerSocket(8989);
        //2.阻塞式监听
        boolean flag = true;
        while(flag){
            Socket client = server.accept();
            System.out.println("与一个客户端建立连接了.............");
            //开启线程对client提供服务
            new Thread(new Channel(client)).start();
        }
        server.close();
    }

    static class Channel implements Runnable{
        Socket client = null;
        DataInputStream is = null;
        DataOutputStream os = null;

        public Channel(Socket client) {
            this.client = client;
            try {
                is = new DataInputStream(client.getInputStream());
                os = new DataOutputStream(client.getOutputStream());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        //读取数据
        public String read(){
            String msg = null;
            try {
                msg = is.readUTF();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return msg;
        }

        //写出
        public void write(String msg){
            try {
                os.writeUTF(msg);
                //6.刷出
                os.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        //关闭
        public void close(){
            if(os!=null){
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            };
            if(is!=null){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            };
            if(client!=null){
                try {
                    client.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            };
        }

        @Override
        public void run() {
            //3.获取输入流,读取客户端发送的数据
            String msg = read();
            //4.处理数据
            String uname=null;
            String upwd=null;
            String[] arr = msg.split("&");
            for(String str:arr){
                String[] arr2 = str.split("=");
                if("username".equals(arr2[0])){
                    uname = arr2[1];
                }else if("password".equals(arr2[0])){
                    upwd = arr2[1];
                }
            }
            //5.获取输出流,将结果响应给客户端
            if("admin".equals(uname) && "1234".equals(upwd)){
                write("登录成功");
            }else{
                write("用户名或密码错误");
            }
            //7.关闭
            close();
        }
    }
}

d run() {
//3.获取输入流,读取客户端发送的数据
String msg = read();
//4.处理数据
String uname=null;
String upwd=null;
String[] arr = msg.split(“&”);
for(String str:arr){
String[] arr2 = str.split(“=”);
if(“username”.equals(arr2[0])){
uname = arr2[1];
}else if(“password”.equals(arr2[0])){
upwd = arr2[1];
}
}
//5.获取输出流,将结果响应给客户端
if(“admin”.equals(uname) && “1234”.equals(upwd)){
write(“登录成功”);
}else{
write(“用户名或密码错误”);
}
//7.关闭
close();
}
}
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值