网络编程复习

题型分布及得分:问答题5道40分;设计题3道50分;论述题10分

一、 问答题(40分 5道)

1. 阻塞的Socket通信的原理是什么?

​ Socket 就像一个电话插座,负责连通两端的电话,进行点对点通信,让电话可以进行通信,端口就像插座上的孔,端口不能同时被其他进程占用。而我们建立连接就像把插头插在这个插座上,创建一个 Socket 实例开始监听后,这个电话插座就时刻监听着消息的传入,谁拨通我这个“IP 地址和端口”,我就接通谁。

实际上,Socket 是在应用层和传输层之间的一个抽象层,它把 TCP/IP 层复杂的操作抽象为几个简单的接口,供应用层调用实现进程在网络中的通信。Socket 起源于 UNIX,在 UNIX 一切皆文件的思想下,进程间通信就被冠名为文件描述符(file descriptor),Socket 是一种“打开—读/写—关闭”模式的实现,服务器和客户端各自维护一个“文件”,在建立连接打开后,可以向文件写入内容供对方读取或者读取对方内容,通讯结束时关闭文件。

2. 阻塞通信中,阻塞在服务端和客户端发生的时机是什么?

  • 服务端:accept 客户端:connect
  • 读操作:当接收缓冲区中没有数据时,读操作会一直阻塞住,直到有数据到来才返回
  • 写操作:发送缓冲区没有空间或者空间不足的话,写操作会直接阻塞住,直到缓冲区有足够的空间为止

3. 如何实现多个客户端的阻塞Socket通信?

  • 服务器端创建ServerSocket,循环调用accept()等待客户端连接
  • 客户端创建一个socket并请求和服务器端连接
  • 服务器端接受客户端请求,创建socket与该客户建立专线连接
  • 建立连接的两个socket在一个单独的线程上对话
  • 服务器端继续等待新的连接

4. TCP和UDP通信的异同,各自有什么优缺点

​ 【这个是我自己总结的】

1> TCP基于连接,UDP无连接

2> TCP对系统资源要求多,UDP少

3> TCP面向字节流,UDP面向报文

4> TCP连接为一对一,UDP支持一对一、一对多、多对一和多对多的交互通信

5> TCP保证数据正确性及顺序,UDP不保证

​ TCP优点: ① 稳定【无差错、不丢失、不重复、按序到达】

​ ② 相对安全【三握四挥机制】

​ ③ 逻辑通信信道为全双工

​ TCP缺点: ① 速度慢

​ ② 资源开销大

​ UDP优点: ① 速度快

​ ② 系统开销小

​ UDP缺点: ① 不可靠,不适用网络差的环境

5. 非阻塞通信的基本原理是什么,阻塞通信和非阻塞通信的区别?

在整个通信过程中读和写操作不会阻塞,当前处理线程不存在阻塞情况。从A机器到B机器它的通信过程是:A机器一条线程将通道设置为写事件后往下执行,而另外一条线程遍历到此通道有字节要写并往socket写数据,B机器一条线程遍历到此通道有字节要读,交给另外一条线程对socket读数据,处理完又把通道设置为写事件,遍历线程遍历到此通道有字节要写,又往socket写数据传往A机器,不断往下循环此操作直到完成通信。这个过程每台机器都有两类主要线程,一类是负责逻辑处理且将通道改为可写或可读事件的线程,另外一类是专门用于遍历通道并负责socket读写的线程,这种方式就是非阻塞IO模式

6. 什么是阿姆尔达加速比定律?什么是伪共享?如何解决伪共享的问题及方法?

  • 阿姆尔达加速比定律:用并行前的执行速度和并行后的执行速度之比来表示的,它表示了在并行化之后的效率提升情况
  • 伪共享:缓存系统中是以缓存行(cache line)为单位存储的。缓存行是2的整数幂个连续字节,一般为32-256个字节。最常见的缓存行大小是64个字节。当多线程修改互相独立的变量时,如果这些变量共享同一个缓存行,就会无意中影响彼此的性能,这就是伪共享
  • 伪共享的解决方法:
    1. 字节填充:也就是创建一个变量的时候使用填充字段填充该变量所在的缓存行,这样就避免了多个变量存在同一个缓存行
    2. 使用sun.misc.Contended注解,用来解决伪共享问题

7. NIO 和传统的I0的区别是什么?有什么优点?

  1. 传统的IO特点

    • 它的各种流是阻塞的。单线程的情况下,只能存在一个客户端。
    • 一个线程调用读写的方法的时候,其它要调用其读写时会被阻塞,直到有一些数据被读取或者数据被完全写入。
    • 它是面向流的。每次是从流中读取一个后者多个字节,直到所有的字节被读完。没有缓冲区,不能前后移动流中的数据。
    • serverSocket和socket都是阻塞式的,因此一旦有大规模的并发行为,而每一个访问都会开启一个新线程。
  2. NIO

    • NIO是为了弥补IO的不足而诞生的,性特性为:非阻塞I/O,选择器,缓冲以及管道。管道(Channel),缓冲(Buffer) ,选择器( Selector)是其主要特征

      • buffer:因为NIO是基于缓冲的,所以buffer是最底层的必要类,这也是IO和NIO的根本不同,虽然stream等有buffer开头的扩展类,但只是流的包装类,还是从流读到缓冲区,而NIO却是直接读到buffer中进行操作。
      • channel:类似于IO的stream,但是不同的是除了FileChannel,其他的channel都能以非阻塞状态运行。FileChannel执行的是文件的操作,可以直接DMA操作内存而不依赖于CPU。其他比如socketchannel就可以在数据准备好时才进行调用。
      • selector:用于分发请求到不同的channel,这样才能确保channel不处于阻塞状态就可以收发消息
    • 在NIO中,数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。NIO的buffer可以使用直接内存缓冲区,该缓冲区不在JVM中,性能会比JVM的缓冲区略好,不过会增加相应的垃圾回收的负担,因为JVM缓冲区的性能已经足够好,所以除非在对缓冲有特别要求的地方使用直接缓冲区,尽量使用JVM缓冲。

8. 缓冲Buffer有如下的一些属性:容量、极限、位置,表示什么?另外,其关系是什么?如何复用?

  • 容量:表示该缓冲区可以保存多少数据
  • 极限:表示缓冲区的当前终点,不能对缓冲区中超过极限的区域进行读写操作。极限是可以修改的,这有利于缓冲区的重用。
  • 位置:表示缓冲区中下一个读写单元的位置,每次读写缓冲区的数据时,都会改变该值,为下一次读写数据做准备。位置是一个非负数,不应该大于极限
  • 以上三个属性的大小关系为:容量>=极限>=位置>=0

?????如何复用

9. 什么是静态代理?什么是动态代理?如何实现动态代理?

  • 静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。

  • 静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。

  • 动态代理是实现 JDK 里的 InvocationHandler 接口的 invoke 方法,但注意的是代理的是接口,也就是业务类必须要实现接口,通过 Proxy 里的 newProxyInstance 得到代理对象。

  • 还有一种动态代理 CGLIB,代理的是类,不需要业务类继承接口,通过派生的子类来实现代理。通过在运行时,动态修改字节码达到修改类的目的。

  • AOP 编程就是基于动态代理实现的,比如著名的 Spring 框架、 Hibernate 框架等等都是动态代理的使用例子。

10. Spring IOC和AOP是什么?各自想解决何种问题?

  • IOC:控制反转,是一种设计模式。一是控制权的转移:由传统的在程序中控制并依赖转移到容器赖控制;第二是依赖注入:将相互以来的对象分离,在Spring配置文件中描述他们的依赖关系。他们的依赖关系只在使用的时候才建立。从而促进松耦合,面向接口编程、非侵入的以及类的注入。
  • AOP:面向切面,是一种编程思想,oop的延续。将系统中非核心的业务提取出来,进行单独处理。将一个共同的功能从不同的类中分离出来,然后将其封装使之可以被其它类使用。面向方面编程是类的织入,比如把日志记录,性能分析,安全性分析,持久化等等织入到业务逻辑中去。
  • Spring的AOP和IOC在项目中都是为了解决系统代码耦合度过高的问题。使代码重用度高,易于维护。比如事务,日志和安全等。

11. Mybatis 的持久性中,并没有DAO层的持久性类的实现,那么Mybatis是如何实现持久性的?

  1. 单纯的基于xml方式,可以没有接口,sql语句写在xml文件中
  2. 单纯的基于注解方式,可以没有xml文件,sql语句写在dao层接口的注解中
  3. xml方式和注解方式相结合的方式,sql语句写在xml文件中

12. Spring Boot的特点是什么?和传统的Spring MVC的结构相比有什么优势?

  • 两者是不同的概念

  • SSM是WEB应用框架,涵盖整个应用层,而spring boot你可以看做一个启动、配置、快速开发的辅助框架,本身针对的是微服务。

  • springboot 只是为了提高开发效率,是为了提升生产力的

  • springboot一个应用是一个可执行jar(启动类main方法启动web应用),而不像传统的war,内嵌tomcat容器,可以jar形式启动一个服务,可以快速部署发布web服务,微服务最好不过了。

  • 将原有的xml配置,简化为java配置

  • 当然结构可能跟一般的ssm有一定区别,但其实主要是在资源文件。

  • Spring Boot 默认“约定”从资源目录的这些子目录读取静态资源,而且支持yml配置文件。

13. 什么是微服务?和单体架构、SOA 架构相比,微服务有何优缺点?

  1. SOA(Service Oriented Architecture)“面向服务的架构”:他是一种设计方法,其中包含多个服务, 服务之间通过相互依赖最终提供一系列的功能。一个服务 通常以独立的形式存在与操作系统进程中。各个服务之间 通过网络调用。

  2. 微服务架构:其实和 SOA 架构类似,微服务是在 SOA 上做的升华,微服务架构强调的一个重点是“业务需要彻底的组件化和服务化”,原有的单个业务系统会拆分为多个可以独立开发、设计、运行的小应用。这些小应用之间通过服务完成交互和集成。

  3. 微服务架构的好处

    • 单个服务很容易开发、理解和维护。
    • 这种架构使得每个服务都可以有专门开发团队来开发。
    • 微服务架构模式是每个微服务独立的部署。
    • 微服务架构模式使得每个服务独立扩展。
  4. 微服务架构的不足

    • 微服务应用是分布式系统,由此会带来固有的复杂性。

    • 服务地址目录,服务健康度,部署困难,服务依赖问题,数据库分区问题。

二、设计题

1. Socket 的单客户端和多客户端的设计和实现。

单客户端服务器:

public static void main(String[] args) 
{
    try {
    	// 初始化服务端socket并且绑定9999端口  
       	ServerSocket serverSocket  =new ServerSocket(9999);//等待客户端的连接 
       	Socket socket = serverSocket.accept(); 
        //获取输入流,并且指定统一的编码格式
        BufferedReader bufferedReader =new BufferedReader(new
        InputStreamReader(socket.getInputStream(),"UTF-8")); //读取一行数据
        String str; 
        //通过while循环不断读取信息
        while ((str = bufferedReader.readLine())!=null){
            //输出打印
            System.out.println(str);
        }
    }catch (IOException e) {
        e.printStackTrace();
    }
}

多客户端服务器:

public class test {
    public static void main(String[] args) throws IOException {
        // 初始化服务端socket并且绑定9999端口
        ServerSocket serverSocket = new ServerSocket(9999);
        while (true) {
            //等待客户端的连接
            Socket socket = serverSocket.accept();                //每当有一个客户端连接进来后,就启动一个单独的线程进行处理
            new Thread(new Runnable() {
                @Override
                public void run() {
                    //获取输入流,并且指定统一的编码格式
                    BufferedReader bufferedReader = null;
                    try {
                        bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
                        //读取一行数据
                        String str;
                        //通过while循环不断读取信息,
                        while ((str = bufferedReader.readLine()) != null) {
                            //输出打印
                            System.out.println("客户端说:" + str);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
}

2. 线程池的工作线程的实现方法。

ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
final int temp = i;
newFixedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getId() + ",i:" + temp);
}
});
}

3. 基于DatagramSocket的通信问题的设计和实现。

Java用两个类实现UDP:DatagramPacket和DatagramSocket,前者将数据字节填充到UDP包,后者收发UDP包。用法很简单,DatagramSocket收发DatagramPacket即可。与TCP不同,UDP的socket并没有客户端和服务端的区别

服务端

public class UDPServer {
    public final static int PORT = 5555;

    public static void main(String[] args) {
        byte[] buffer = new byte[64];
        //侦听某个UDP端口
        try (DatagramSocket server = new DatagramSocket(PORT)) {
            server.setSoTimeout(10000);
            while (true) {
                DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
                try {
                    server.receive(packet);//10s超时接收
                    byte[] now = new Date().toString().getBytes("US-ASCII");
                    //组织数据,并传入请求方socket地址作为回应地址
                    DatagramPacket outgoingPacket = new DatagramPacket(now, now.length, packet.getAddress(), packet.getPort());
                    server.send(outgoingPacket);
                } catch (SocketTimeoutException ex) {
                    System.out.println("Timeout!");//超时
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        } catch (SocketException ex) {
            ex.printStackTrace();
        }
    }
}

客户端:

public class UDPClient {
    public final static int PORT = 5555;
    public final static String HOST = "localhost";

    public static void main(String[] args) {
        try (DatagramSocket socket = new DatagramSocket()) {//绑定端口由系统分配
            //DatagramSocket依赖于数据报DatagramPacket收发数据,byte[1]是为了触发服务端
            DatagramPacket packet = new DatagramPacket(new byte[1], 1, InetAddress.getByName(HOST), PORT);
            socket.setSoTimeout(10000);//receive数据阻塞等待10s,超过这个时间仍未收到数据说明丢失了
            socket.send(packet);

            DatagramPacket incomePacket = new DatagramPacket(new byte[64], 64);
            try {
                socket.receive(incomePacket);//阻塞接收,10s超时
                byte[] nowByte = new byte[incomePacket.getLength()];
                System.arraycopy(incomePacket.getData(), 0, nowByte, 0, incomePacket.getLength());
                String now = new String(nowByte, "US-ASCII");
                System.out.println(now);
            } catch (SocketTimeoutException ex) {
                System.out.println("Timeout!");
            }

        } catch (IOException ex) {
            return;
        }
    }
}

4. 基于通道的非阻塞通信的服务端和客户端的设计与实现。

server

public class NIOServer1 {
	// 本地字符集
	private static final String LocalCharSetName = "UTF-8";
 
	// 本地服务器监听的端口
	private static final int Listenning_Port = 8888;
 
	// 缓冲区大小
	private static final int Buffer_Size = 1024;
 
	// 超时时间,单位毫秒
	private static final int TimeOut = 3000;
 
	public static void main(String[] args) throws IOException {
		// 创建一个在本地端口进行监听的服务Socket信道.并设置为非阻塞方式
		ServerSocketChannel serverChannel = ServerSocketChannel.open();
 
		serverChannel.socket().bind(new InetSocketAddress(Listenning_Port));
		serverChannel.configureBlocking(false);
 
		// 创建一个选择器并将serverChannel注册到它上面
		Selector selector = Selector.open();
		serverChannel.register(selector, SelectionKey.OP_ACCEPT);
 
		while (true) {
			// 等待某个信道就绪
			if (selector.select(TimeOut) == 0) {
				System.out.println(".");
				continue;
			}
 
			// 获得就绪信道的键迭代器
			Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();
 
			// 使用迭代器进行遍历就绪信道
			while (keyIter.hasNext()) {
				SelectionKey key = keyIter.next();
				// 这种情况是有客户端连接过来,准备一个clientChannel与之通信
				if (key.isAcceptable()) {
					SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept();
					clientChannel.configureBlocking(false);
					clientChannel.register(selector, SelectionKey.OP_READ,
							ByteBuffer.allocate(Buffer_Size));
				}
 
				// 客户端有写入时
				if (key.isReadable()) {
					// 获得与客户端通信的信道
					SocketChannel clientChannel = (SocketChannel) key.channel();
 
					// 得到并重置缓冲区的主要索引值
					ByteBuffer buffer = (ByteBuffer) key.attachment();
					buffer.clear();
 
					// 读取信息获得读取的字节数
					long bytesRead = clientChannel.read(buffer);
 
					if (bytesRead == -1) {
						// 没有读取到内容的情况
						clientChannel.close();
					} else {
						// 将缓冲区准备为数据传出状态
						buffer.flip();
						// 将获得字节字符串(使用Charset进行解码)
						String receivedString = Charset
								.forName(LocalCharSetName).newDecoder().decode(buffer).toString();
 
						// 控制台打印出来
						System.out.println("接收到信息:" + receivedString);
 
						// 准备发送的文本
						String sendString = "你好,客户端. 已经收到你的信息" + receivedString;
 
						// 将要发送的字符串编码(使用Charset进行编码)后再进行包装
						buffer = ByteBuffer.wrap(sendString.getBytes(LocalCharSetName));
 
						// 发送回去
						clientChannel.write(buffer);
 
						// 设置为下一次读取或是写入做准备
						key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
					}
				}
 
				keyIter.remove();
			}
		}
 
	}
}

client

public class NIOClient1Test {
 
	public static void main(String[] args) throws UnknownHostException,
			IOException {
		Socket s = new Socket("localhost", 8888);
 
		InputStream inStream = s.getInputStream();
		OutputStream outStream = s.getOutputStream();
 
		// 输出
		PrintWriter out = new PrintWriter(outStream, true);
		out.println("getPublicKey你好!");
		out.flush();
 
		s.shutdownOutput();// 输出结束
 
		// 输入
		Scanner in = new Scanner(inStream);
 
		StringBuilder sb = new StringBuilder();
		while (in.hasNextLine()) {
			String line = in.nextLine();
			sb.append(line);
		}
 
		String response = sb.toString();
		System.out.println("response=" + response);
 
	}
}

5. 求和、求pi、排序问题的并行(多线程)的实现。

6. 静态代理类的设计和实现。(计算)

7. 动态代理类的设计与实现。(计算)

8. 数据库连接池DBConnectionPool的实现。

三、论述题

1. 你使用过哪些框架?这些框架的优缺点是什么?你在何处使用过这些框架?

使用过spring mvc 和 mybaits ,spring boot,spring等。

spring mvc优点:

  1. 使用简单,学习成本低。
  2. 很容易就可以写出性能优秀的程序.。
  3. 灵活性强,Spring MVC的框架易扩展SpringMVC的。

缺点:

  1. Spring与MVC 的Servlet API 耦合,难以脱离容器独立运行
  2. 太过于细分,开发效率低
  3. 过度追求完美,有过度设计的危险解决的问题

mybatis优点:

  1. 易于上手和掌握。
  2. sql写在xml里,便于统一管理和优化。
  3. 解除sql与程序代码的耦合。
  4. 提供映射标签,支持对象与数据库的orm字段关系映射
  5. 提供对象关系映射标签,支持对象关系组建维护
  6. 提供xml标签,支持编写动态sql。

缺点:

  1. sql工作量很大,尤其是字段多、关联表多时,更是如此。
  2. sql依赖于数据库,导致数据库移植性差。
  3. 由于xml里标签id必须唯一,导致DAO中方法不支持方法重载。
  4. 字段映射标签和对象关系映射标签仅仅是对映射关系的描述,具体实现仍然依赖于sql。(比如配置了一对多Collection标签,如果sql里没有join子表或查询子表的话,查询后返回的对象是不具备对象关系的,即Collection的对象为null)
  5. DAO层过于简单,对象组装的工作量较大。
  6. 不支持级联更新、级联删除。
  7. 编写动态sql时,不方便调试,尤其逻辑复杂时。 8 提供的写动态sql的xml标签功能简单(连struts都比不上),编写动态sql仍然受限,且可读性低。
  8. 参数的数据类型支持不完善。(如参数为Date类型时,容易报没有get、set方法,需在参数上加@param)

spring boot优点

  1. 快速构建项目
  2. 对主流开发框架的无配置集成
  3. 项目可独立运行,无需外部依赖 Servlet 容器
  4. .提供运行时的应用监控
  5. 极大地提高了开发、部署效率
  6. 与云计算的天然集成

缺点:

  1. 版本迭代速度很快,一些模块改动很大
  2. .由于不用自己做配置,报错时很难定位
  3. 网上现成的解决方案比较少

sping优点

  1. 提供了一种管理对象的方法,可以把中间层对象有效地组织起来。一个完美的框架“黏合剂”。

  2. 采用了分层结构,可以增量引入到项目中。

  3. 有利于面向接口编程习惯的养成。

  4. 目的之一是为了写出易于测试的代码。

  5. 非侵入性,应用程序对Spring API的依赖可以减至最小限度。

  6. 一致的数据访问介面。

  7. 一个轻量级的架构解决方案。

缺点:

  1. 中断了应用程序的逻辑,使代码变得不完整,不直观。此时单从Source无法完全把握应用的所有行为。
  2. 将原本应该代码化的逻辑配置化,增加了出错的机会以及额外的负担。
  3. 调试阶段不直观,后期的bug对应阶段,不容易判断问题所在。

使用:

2. 云平台当中的Saas、PaaS、 las 是什么?你对此的理解。

IaaS: Infrastructure-as-a-Service(基础设施即服务)

第一层叫做IaaS,有时候也叫做Hardware-as-a-Service,几年前如果你想在办公室或者公司的网站上运行一些企业应用,你需要去买服务器,或者别的高昂的硬件来控制本地应用,让你的业务运行起来。
  但是现在有IaaS,你可以将硬件外包到别的地方去。IaaS公司会提供场外服务器,存储和网络硬件,你可以租用。节省了维护成本和办公场地,公司可以在任何时候利用这些硬件来运行其应用。
  一些大的IaaS公司包括Amazon, Microsoft, VMWare, Rackspace和Red Hat.不过这些公司又都有自己的专长,比如Amazon和微软给你提供的不只是IaaS,他们还会将其计算能力出租给你来host你的网站。

PaaS: Platform-as-a-Service(平台即服务)

第二层就是所谓的PaaS,某些时候也叫做中间件。你公司所有的开发都可以在这一层进行,节省了时间和资源。
  PaaS公司在网上提供各种开发和分发应用的解决方案,比如虚拟服务器和操作系统。这节省了你在硬件上的费用,也让分散的工作室之间的合作变得更加容易。网页应用管理,应用设计,应用虚拟主机,存储,安全以及应用开发协作工具等。
  一些大的PaaS提供者有Google App Engine,Microsoft Azure,Force.com,Heroku,Engine Yard。最近兴起的公司有AppFog, Mendix 和 Standing Cloud

SaaS: Software-as-a-Service(软件即服务)

第三层也就是所谓SaaS。这一层是和你的生活每天接触的一层,大多是通过网页浏览器来接入。任何一个远程服务器上的应用都可以通过网络来运行,就是SaaS了。
  你消费的服务完全是从网页如Netflix, MOG, Google Apps, Box.net, Dropbox或者苹果的iCloud那里进入这些分类。尽管这些网页服务是用作商务和娱乐或者两者都有,但这也算是云技术的一部分。
  一些用作商务的SaaS应用包括Citrix的GoToMeeting,Cisco的WebEx,Salesforce的CRM,ADP,Workday和SuccessFactors。

3. 阅读以下材料,设计面向广大企业用户的物联网平台,给出你的理解和方案。

本项目通过搭建XXX物联网云平台,为企业用户的能源提供机房服务、计算服务、存储服务、备份服务、安全服务、网络服务、管理平台服务。系统迁移,支持服务和应用集中部署的服务能力。主要内容如下:

a. 搭建xx物联云平台,…

b. 向用户提供统一的物联网系统应用,如用户管理、系统监控、设备管理、环境管理、能源信息展示等

c. 向管理者提供统一的运维管理和大数据运营分析*

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值