JAVA网络编程

 

目录

一、ip

1.ipv4

2.ipv6

二、inteAddress类的使用

三、端口

四、协议

1.UDP协议

2.TCP协议

五、UDP发送端/UDP接收端

1.UDP发送端---使用的为DatagramSocket对象

2.UDP接收端---也是创建DatagramSocket对象

六、UDP综合练习(发送端可以一直发送)

七、UDP三种通讯方式

1.点播

2.组播

3.广播

八、TCP客户端/服务器(发送端/接收端)

 九、三次握手和四次挥手

1.三次握手

 2.四次挥手

 十、TCP通信协议练习

1.练习一:服务器端会返回一句信息(用到IO流转换)

2.练习二:读取本地文件传给服务器并接收回应

十一、TCP练习题二的服务器弊端一(覆盖原路径)

1.UUID引入

2.UUID解决上述问题的实现

十二、TCP练习题二的服务器弊端二(服务器无法同时处理多个客户端发送)

十三、对十二解决方案的进一步优化(线程池)


网络编程三要素:

ip:设备在网络中的地址,是唯一标识。(设备:电脑、手机等等)

端口:应用程序在设备中的唯一标识

协议:数据在网络中传输的规则,常见的协议有UDP和TCP协议

一、ip

全程“互联网协议地址”,也称ip地址。是分配给上网设备的数字标签。常见的ip分类为:ipv4和ipv6,我们想要链接哪台电脑,我们就找到该电脑ip就可以

1.ipv4

先明白换算关系 1Byte(字节) = 8bit(位)  ipv4是由32bit(4Byte)组成

举例:11000000 10101000 00000001 01000010 ----根据点分十进制表示法-->192.168.1.66

点分十进制:就是把每一个字节先转换位十进制,最后每个字节中间用 . 连接

注意:ipv4逐渐满足不了世界级用户使用,因为每个字节表示范围[0--255],也就是一个字节能表示256位,那32位的ipv4最多只能表示256*256*256*256 = 42亿+。数量逐渐满足不了用户数。故此使用ipv6

2.ipv6

采用128bit位地址长度,每16个bit位也就是2个Byte字节,分为一组,总共8组。比如:

二、inteAddress类的使用

为了方便我们对ip地址的获取和操作,所提供的一个类

inteAddress:此类表示internet协议(ip)地址

常用方法及其使用:

import java.net.InetAddress;
import java.net.UnknownHostException;

public class Demo1 {
    public static void main(String[] args) throws UnknownHostException {
        /*
        InteAddress类的常用方法
        1.static InteAddress getByName(String host);
        确定主机名称ip的地址,主机名称可以是机器名称,也可以是ip地址
        2.String getHostName();
        获取ip地址的主机名
        3.String getHostAddress();
        返回文本显示的ip地址字符串
         */
        InetAddress byName = InetAddress.getByName("xxx.xxx.x.xx");

        String hostName = byName.getHostName();
        System.out.println("主机名为:"+hostName);

        String hostAddress = byName.getHostAddress();
        System.out.println("ip为:"+hostAddress);
    }
}

三、端口

端口是设备与外界通讯数据的出口,是应用程序在设备中的唯一标识,一个端口只能被一个应用端口绑定。

端口号:用两个字节表示的整数,取值范围[0---65535],其中0--1023的一些端口用于一些知名网络服务或者应用。我们自己写代码,只能使用1024以上的,且小于等于65535

四、协议

计算机网络中, 连接和通信的规则被称为网络通信协议,分为UDP和TCP

1.UDP协议

①用户数据报协议(User Datagram Protocol)

②UDP是面向无连接通信协议(无连接:不管发送端和接收端是否连接成功)

特点:速度快、有大小限制一次最多64k,数据不安全,容易丢失。像一般音频或者视频会使用此协议。

2.TCP协议

①传输控制协议(Transmission Control Protocol)

②TCP协议是面向连接的通信协议(面向连接:发送端和接收端必须产生连接)

特点:速度慢、没有大小限制、数据安全

五、UDP发送端/UDP接收端

1.UDP发送端---使用的为DatagramSocket对象

由于UDP采用无连接通信,所以发送端需要做的步骤有:①.找码头,②.打包,③.由码头发送包裹,④.完成发送

import java.io.IOException;
import java.net.*;

public class UdpDemo1 {
    /*
    UDP发送端代码实现
     */
    public static void main(String[] args) throws IOException {
        //1.找码头,创建UDP码头对象new DatagramSocket()
        DatagramSocket ds = new DatagramSocket();

        //2.打包信息,创建打包对象
        //new DatagramPacket(四个参数)
        //参数一:发送的内容数组
        String s = "祝你新年快乐,2022年1月5日,23点24分";
        byte[] bytes = s.getBytes();
        //参数二:内容数组里需要发送的内容长度
        //参数三:本机的ip/地址
        InetAddress address = InetAddress.getByName("127.0.0.1");
        //参数四:端口号
        int port = 10000;
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);

        //3.码头发送,让打包的信息dp和码头ds发生联系
        ds.send(dp);

        //4.完成发送,释放资源
        ds.close();
    }
}

2.UDP接收端---也是创建DatagramSocket对象

接收端需要进行的步骤:①找到对应码头,传入对应端口号、②拿一个新的包、③去码头接收并且将礼物放入新包中、④从新包中获得礼物、⑤接收结束

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

public class UdpDemo2 {
    /*
    UDP接收端代码实现
     */
    public static void main(String[] args) throws IOException {
        //1.找码头,参数为发送端目的地的端口号
        DatagramSocket ds = new DatagramSocket(10001);

        //2.找个新箱子,传递两个参数
        byte[] bytes = new byte[1024];
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length);

        //3.去码头接收礼物,然后将礼物放入新箱子
        ds.receive(dp);//表示码头拿这个箱子去接收数据

        //4.从新箱子中获得礼物
        byte[] data = dp.getData();
        /*
        注意:此时的接收端获得的内容,会有很多null
        所以我们需要优化,优化思路就是确定箱子中含有数据的长度
        最后打印传入new String();参数中去
         */
        int length = dp.getLength();
        System.out.println(new String(data,0,length));
        //5.最后完成礼物接收
        ds.close();
    }
}

 注意:创建好发送端和接收端之后,先运行接收端!!!如果接收端一直没有接收到东西,就会一直死等(阻塞)。

六、UDP综合练习(发送端可以一直发送)

题目要求:发送端可以一直键盘录入发送,一直等到发送“886”之后,才会停止发送。而接收端可以接收到发送端的信息

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

public class ClientDemo {
    /*
    发送端
     */
    public static void main(String[] args) throws IOException {
        //由于发送端要用到键盘录入,所以创建Scanner对象
        Scanner sc = new Scanner(System.in);

        //1.找码头
        DatagramSocket ds = new DatagramSocket();

        //2.打包信息,最后封装到DatagramPacket对象中
        while (true) {
            //要打包的内容
            String s = sc.nextLine();
            if("886".equals(s)){
                break;
            }
            byte[] bytes = s.getBytes();
            //确认接收端的ip地址
            InetAddress address = InetAddress.getByName("127.0.0.1");
            //确认接收端的码头端口号
            int port = 10000;
            DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);

            //3.让码头将包发送
            ds.send(dp);
        }
        //4.发送完毕
        ds.close();
    }
}
---------------------------------------------------------------------------------------
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class ServerDemo {
    /*
    接收端
     */
    public static void main(String[] args) throws IOException {
        //1.找到码头,要根据传入端口号跟核实码头是否正确
        DatagramSocket ds = new DatagramSocket(10000);

        while (true) {
            //2.创建一个新的包,并设置包传输一次的内容大小
            byte[] bytes = new byte[1024];
            DatagramPacket dp = new DatagramPacket(bytes,bytes.length);

            //3.用新包在码头上接收发过来的礼物
            ds.receive(dp);

            //4.将新包中的礼物进行解析
            byte[] data = dp.getData();
            int len = dp.getLength();
            System.out.println(new String(data,0,len));
        }
        /*
        接收完毕,离开码头,
        一般我们在接收端完毕后,就会调用close(),但是由于我们要求
        发送端会一直发送东西,所以我们这里不关闭
         */
    }
}

注意:该发送端可以一次打开多个,接收端都可以接收的到

七、UDP三种通讯方式

我们可以根据UDP协议接收端数量的不同,去分为三种通讯形式:

①点播;②组播(只在ipv4中的概念,在ipv6中叫多播);③广播

1.点播

(上述的综合练习,就相当于点播)

2.组播

一个发送端 ——> 一组接收端

组播地址:224.0.0.0 --- 239.255.255.255,其中224.0.0.0 --- 224.0.0.255为预留的组播地址,我们不可用!

Ⅰ---发送端步骤:①创建码头对象;②创建数据,并且打包;③由码头发送到指定组播地址;④发送完成.close();

Ⅱ---接收端步骤①创建MulticastSocket()对象;②创建箱子,接收数据;③将当前电脑添加到组中;④调用DatagramSocket中的方法receive()去接收并且存放至箱子;⑤解析箱子内容,并打印控制台;⑥接收完毕.close();

    /*
    发送端
     */
    public static void main(String[] args) throws IOException {
        //1.创建码头
        DatagramSocket ds = new DatagramSocket();

        //2.将发送内容并打包打包
        //内容
        String s = "hello,组播方式";
        byte[] bytes = s.getBytes();
        //组播ip地址
        InetAddress byName = InetAddress.getByName("224.0.1.0");
        //指定端口号
        int port = 10002;
        //打包
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length,byName,port);

        //3.发送
        ds.send(dp);

        //4.发送结束
        ds.close();
    }
---------------------------------------------------------------------------------
    /*
    接收端
     */
    public static void main(String[] args) throws IOException {
        //1.创建MulticastSocket对象,传入对应端口
        MulticastSocket ms = new MulticastSocket(10002);

        //2.创建个新的箱子
        byte[] bytes = new byte[1024];
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length);

        //3.把当前计算机绑定一个组播地址
        ms.joinGroup(InetAddress.getByName("224.0.1.0"));

        //4.接收数据
        ms.receive(dp);

        //5.获得数据内容,然后放入新箱子,最后输出
        byte[] data = dp.getData();
        int length = dp.getLength();
        System.out.println(new String(data,0,length));

        //6.结束接收
        ms.close();
    }

3.广播

发送端步骤:和组播差不多,但是就是把组播ip改为广播用的ip(255.255.255.255);

接收端:这个时候接收端对象还是用DatagramSocket对象,注意端口一致就可以

八、TCP客户端/服务器(发送端/接收端)

定义:TCP通信协议是一种可靠的网络协议,它在通信的两端个建立一个Socket对象

客户端的对象为:Socket

接收端的对象为:ServerSocket;

-----在通信之前要保证已经建立了连接,然后通过Socket产生IO流来进行通信。

    /*
    客户端(发送端)建立
     */
    public static void main(String[] args) throws IOException {
        //1.创建客户端对象,传入ip,和端口号
        Socket socket = new Socket("127.0.0.1",10004);

        //2.获取一个IO流开始写数据,这里获得的为一个往外写的字节流
        OutputStream os = socket.getOutputStream();
        //所以这里写的应该是个字节数据
        os.write("TCP123".getBytes());

        //3.释放资源
        os.close();
        socket.close();
    }
------------------------------------------------------
    /*
    服务器(接收端)建立
     */
    public static void main(String[] args) throws IOException {
        //1.创建服务器对象ServerSocket,传入客户端一致的端口
        ServerSocket ss = new ServerSocket(10004);

        //2.等待客户端连接,如果连接成功则返回一个Socket对象,如果没有返回
        //那么代码就会在这一行死等(阻塞)
        Socket accept = ss.accept();

        //3.获得输入流对象
        InputStream is = accept.getInputStream();
        /*
        注意:我们的is一次只能读一个字节,
        所以我们需要建立循环去读
         */
        int b;
        while((b = is.read()) != -1){
            System.out.print((char) b);//记得转换为char
        }

        //4.释放资源
        is.close();
        ss.close();
    }

注意:也是要先执行服务器!!!

总结:

 九、三次握手和四次挥手

1.三次握手

 2.四次挥手

为什么会有四步,是因为要多出一步去处理已经建立的数据传输通道

 十、TCP通信协议练习

1.练习一:服务器端会返回一句信息(用到IO流转换)

要求:客户端发送给服务器的消息过后,服务器会返回一句信息

import java.io.*;
import java.net.Socket;

public class TcpDemo2A {
    /*
    练习题:客户端---"这个是客户端发送的一句话123HOHO!"
    练习题要求:当客户端发送了一句话之后,服务器会返回一句话

     */
    public static void main(String[] args) throws IOException {
        //1.创建客户端对象
        Socket socket = new Socket("127.0.0.1",10005);
        //2.获得一个io流开始往出去写数据
        OutputStream os = socket.getOutputStream();
        os.write("这个是客户端发送的一句话123HOHO!".getBytes());
        //这里也要添加个标记,输出流标记
        socket.shutdownOutput();

        /*
        根据题干:我们需要再这里接收服务器返回的信息
        由于返回的为一个字符串,我们将字节流强转至字符流
         */
        BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String s;
        while((s = reader.readLine()) != null){
            System.out.print(s);
        }

        //3.释放资源
        socket.close();
        os.close();
        reader.close();
    }
}
-----------------------------------------------------------
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TcpDemo2B {
    /*
    练习题:服务器---"服务器已经接收到了!123"
     */
    public static void main(String[] args) throws IOException {
        //1.创建服务器对象
        ServerSocket ss = new ServerSocket(10005);

        //2.调用accept()等待客户端连接
        Socket accept = ss.accept();

        //3.获得一个往里读的对象并读取
        //最开始我们会用下面这个方式,但是客户端发送的为字符串的时候,容易出现编解码错误
//        InputStream is = accept.getInputStream();
//        int b;
//        while((b = is.read())!=-1){
//            System.out.print((char)b);
//        }
        //故此我们再这里对字节输入流进行一个强转,转成字符流读取
        BufferedReader reader = new BufferedReader(new InputStreamReader(accept.getInputStream()));
        String s;
        while((s = reader.readLine())!= null){
            System.out.print(s);
        }
        //如果此处我们没有添加输入流标记,那么下面的返回语句也没机会发送给客户端(阻塞)
        accept.shutdownInput();
        /*
        注意题干:当我们接收到客户端发送的消息,我们需要返回一个消息
        所以这里我们要用到Socket类中的字节输出流去写
         */
        OutputStream os = accept.getOutputStream();
        os.write("服务器已经接收到了!123".getBytes());

        //4.释放资源
        ss.close();
        accept.close();
        reader.close();
        os.close();
    }
}

2.练习二:读取本地文件传给服务器并接收回应

import java.io.*;
import java.net.Socket;

public class TcpDemo3A {
    /*
    客户端
    要求:把本地文件a.jpg读取给服务器
    让服务器返回一个"上传成功";提示
     */
    public static void main(String[] args) throws IOException {
        //1.创建客户端对象
        Socket socket = new Socket("127.0.0.1",10006);

        //2.创建本地流,关联本地文档读取
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("inter\\TCP练习发送本地文件\\a.jpg"));

        //创建网络流,去发送给服务器,os相当于一个字节流,所以我们用一个包装类,提高效率
        OutputStream os = socket.getOutputStream();
        BufferedOutputStream bos = new BufferedOutputStream(os);

        int b;
        while((b = bis.read()) != -1){
            bos.write(b);//通过网络写到服务器
        }
        //记住要给服务器一个结束标记
        socket.shutdownOutput();

        //由于这里的服务器端还返回的有信息
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String line;
        while((line = br.readLine())!= null){
            System.out.print(line);
        }

        socket.close();
        bis.close();
    }
}
------------------------------------------------------------
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class TcpDemo3B {
    /*
    服务器端
    接收客户端发的文件,然后返回一句信息
     */
    public static void main(String[] args) throws IOException {
        //1.创建服务器端对象
        ServerSocket ss = new ServerSocket(10006);

        //2.等待客户端连接
        Socket accept = ss.accept();

        //创建两个流,一个读,一个写
        //第一个流为网络中的流,读取客户端发送的信息
        BufferedInputStream bis = new BufferedInputStream(accept.getInputStream());
        //第二个流为本地的流,去写入
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("inter\\TCP练习发送本地文件\\b.jpg"));

        int b;
        while((b = bis.read())!=-1){
            bos.write(b);
        }

        //给客户端返回一个接收到的信息
        OutputStream os = accept.getOutputStream();
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));//加快效率
        bw.write("成功");
        bw.newLine();
        bw.flush();

        //3.释放资源,注意我们这里只用关闭本地流和服务器端就行,网络流在服务器端对象中,就不用单独关了
        ss.close();
        accept.close();
        bos.close();

    }
}

注意:我们可以对服务器进行死循环优化,让客户端发送一次之后还可以继续发送,参考本文章六条目!!!

十一、TCP练习题二的服务器弊端一(覆盖原路径)

1.UUID引入

弊端:当我们在客户端进行了一次网络传输之后,我们的服务器在新建本地文档的时候,第二次上传文件时,会原来的文件进行覆盖!

解决思路:我们需要一个随机生成且不重复的方法去生成新的文件名

所以引出了一个类:UUID

import java.util.UUID;
public class UUIDDemo {
    /*
    UUID的使用方法
     */
    public static void main(String[] args) {
        int count = 0;
        while (count<3) {
            UUID uuid = UUID.randomUUID();
            String s = uuid.toString();
            count++;
            System.out.println(s);
        }
    }
}

生成的是随机且不重复的,而且可以用toString();输出为字符串

2.UUID解决上述问题的实现

//第二个流为本地的流,去写入
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("inter\\TCP练习发送本地文件\\"+ UUID.randomUUID().toString()+".jpg"));

就是将上述服务器端的bos中的路径名加入生成的UUID字符串

十二、TCP练习题二的服务器弊端二(服务器无法同时处理多个客户端发送)

问题描述:如果仅仅使用while(true){},只解决了服务器可以接收多个客户端的连接请求,但是没有解决同时跟多个客户端的连接请求和传输!!!!

解决思路:采用多线程,将服务器接收到客户端连接之后的操作写成一个线程类

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;

public class TcpDemo3B {

    public static void main(String[] args) throws IOException {
        //1.创建服务器端对象
        ServerSocket ss = new ServerSocket(10006);

        while (true) {
            //2.等待客户端连接
            Socket accept = ss.accept();
            /*
            思路:当服务器接收到客户端连接了之后,创建一个线程
            去进行操作。
             */
            ThreadSocket ts = new ThreadSocket(accept);
            new Thread(ts).start();
        }
        //ss.close();
    }
}
//创建一个服务器的线程类
class ThreadSocket implements Runnable{
    private Socket acceptSocket;
    public ThreadSocket(Socket accept) {
        this.acceptSocket = accept;
    }
    @Override
    public void run() {
        BufferedOutputStream bos = null;
        try {
            //第一个流为网络中的流,读取客户端发送的信息
            BufferedInputStream bis = new BufferedInputStream(acceptSocket.getInputStream());
            //第二个流为本地的流,去写入
            bos = new BufferedOutputStream(new FileOutputStream("inter\\TCP练习发送本地文件\\"
                    + UUID.randomUUID().toString()+".jpg"));
            int b;
            while((b = bis.read())!=-1){
                bos.write(b);
            }
            //给客户端返回一个接收到的信息
            OutputStream os = acceptSocket.getOutputStream();
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));//加快效率
            bw.write("成功");
            bw.newLine();
            bw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(bos!=null){
                //对这个加try{}catch{}的时候要进行非空判断
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(acceptSocket!=null){
                try {
                    acceptSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

十三、对十二解决方案的进一步优化(线程池)

问题描述:使用多线程确实解决了服务器同时跟多个客户端的连接和传输,但是资源消耗太大

解决思路:利用自定义线程池(JAVA多线程Ⅱ高级),我们去对服务器进行最终优化

public static void main(String[] args) throws IOException {
        //1.创建服务器端对象
        ServerSocket ss = new ServerSocket(10006);
        //线程池优化
        ThreadPoolExecutor tp = new ThreadPoolExecutor(
                1,//核心线程
                3,//最大线程数
                60,//临时线程存活时间
                TimeUnit.SECONDS,//存活时间单位
                new ArrayBlockingQueue<>(5),//阻塞队列,最多允许5个线程排队
                Executors.defaultThreadFactory(),//创建线程的方式
                new ThreadPoolExecutor.AbortPolicy()//拒绝策略:一、默认方式(带有抛出异常的)
        );
        while (true) {
            Socket accept = ss.accept();
            ThreadSocket ts = new ThreadSocket(accept);
            //用线程池优化
            tp.submit(ts);
            //注意:由于要随时等待客户端发送,所以这里不关闭线程池
            //tp.shutdown();
        }
        //ss.close();
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java网络编程是一种通过使用Java编程语言,在计算机网络上实现通信的技术。它涉及到协议、IP地址和端口号等三个重要要素。Java编写的应用程序可以在单个计算机上运行,也可以在网络服务器端和客户端上分布运行。此外,Java还可以用于编写小型应用程序模块或applet,作为网页的一部分使用。applet可以嵌入网页中,并在浏览器中运行。在Java中,使用URI可以代表绝对的或相对的资源,而URL则包含了定位资源的信息,并且不能是相对的。Java提供了一系列的类和接口,例如InetAddress、Socket、ServerSocket、URL和URLConnection等,用于实现各种网络应用。通过使用这些类和接口,可以创建连接客户端和服务器之间的套接字,并实现面向连接的通信,保证数据的可靠传输。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Java网络编程](https://blog.csdn.net/weixin_42784609/article/details/130388655)[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^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [超详细入门到精通自学视频课程(阶段01:JavaSE基础02 编程思维编程思维和编程能力、综合应用专题-04、案例...](https://download.csdn.net/download/weixin_54787054/88224199)[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^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [网络编程](https://blog.csdn.net/zhixingwu/article/details/103226003)[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^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值