网络篇面试题

网络篇面试题

1. 打开浏览器访问www.baidu.com 发生了什么事情

当我们在浏览器地址栏中输入baidu.com访问后会自动跳转到百度首页展现页面,发生了以下这些事情

  1. 浏览器查找该域名的 IP 地址
  2. 浏览器和对应的IP建立TCP连接
  3. 浏览器根据解析得到的IP地址向 web 服务器发送一个 HTTP 请求
  4. 服务器收到请求并进行处理
  5. 服务器返回一个响应
  6. 浏览器对该响应进行解码,渲染显示。
  7. 页面显示完成后,浏览器发送异步请求。

1.1 域名解析的流程

浏览器是如何查找域名对应的IP地址的呢?

  1. IP地址是指互联网协议地址,每个处于互联网中的设备都有IP 地址,形如 192.168.0.1 局域网 IP和公网 IP 是有差别的,每个主机在网络中都是IP为标识的,IP才是主机在网络中的位置,域名只是为了方便用户记忆而已,这就要求浏览器能够识别域名并且将其转化为对应的IP地址
  2. 所以浏览器会有一个DNS缓存,其中记录了一些域名与IP的对应关系,供浏览器快速查找需要的IP,但是这个DNS缓存不可能存下所有的域名-IP地址,何况IP地址有时候还会变化,因此当在浏览器DNS缓存中没有找到的时候,就要先向DNS服务器请求域名解析,DNS域名解析时用的是UDP协议

1.2 http使用了tcp协议吗

http使用了tcp协议吗,http协议的特点是什么

  1. TCP协议对应于传输层,而HTTP协议对应于应用层,从本质上来说,二者没有可比性。
  2. Http协议是建立在TCP协议基础之上的,当浏览器需要从服务器获取网页数据的时候,会发出一次Http请求
  3. Http会通过TCP建立起一个到服务器的连接通道,当本次请求需要的数据完毕后,Http会立即将TCP连接断开,这个过程是很短的。所以Http连接是一种短连接,是一种无状态的连接。
  4. 所谓的无状态,是指浏览器每次向服务器发起请求的时候,不是通过一个连接,而是每次都建立一个新的连接。如果是一个连接的话,服务器进程中就能保持住这个连接并且在内存中记住一些信息状态。而每次请求结束后,连接就关闭,相关的内容就释放了,所以记不住任何状态,成为无状态连接。

1.3 三次握手

在这里插入图片描述
TCP三次握手

1.3.1 第一次握手

客户端向服务端发送连接请求报文段。该报文段的头部中SYN=1,ACK=0,seq=x。请求发送后,客户端便进入SYN-SENT状态。

  1. 向目的主机发送TCP连接请求报文
  2. 该TCP报文中SYN标志位为1,产生一个随机数x,表示连接请求,x为本次TCP通信的字节流的初始
    序号。
  3. 该TCP报文通过获取的ip(DNS)找到服务器主机,然后获得MAC地址(ARP),通过网关,最终
    到达目的主机。
    TCP规定:SYN=1的报文段不能有数据部分,但要消耗掉一个序号。
1.3.2 第二次握手

服务端收到连接请求报文段后,如果同意连接,则会发送一个应答:SYN=1,ACK=1,seq=y,ack=x+1
该应答发送完成后便进入SYN-RCVD状态。

  1. 目的主机收到数据帧后,通过ip协议传输帧,再到TCP协议,封装成请求应答报文;
  2. 该报文中SYN标志为1,产生一个随机数y,ack标志位x+1,表示连接请求应答
  3. 该请求应答报文通过接收到的源ip->Mac(arp)->网关,发送到我的主机;
1.3.3 第三次握手

当客户端收到连接同意的应答后,还要向服务端发送一个确认报文段,表示:服务端发来的连接同意应答已经成功收到。
该报文段的头部为:ACK=1,seq=x+1,ack=y+1。 客户端发完这个报文段后便进入ESTABLISHED状态,服务端收到这个应答后也进入ESTABLISHED状态,此时连接的建立完成

  1. 我的主机收到数据帧,通过ip协议传输帧,再到TCP协议,封装成请求确认报文
  2. 该请求确认报文通过目标ip->Mac(arp)->网关,发送到目的主机
  3. 请求确认报文的ack为y +1,表示请求确认;
  4. 目的主机接收到数据帧,连接建立完成

1.4 为什么必须是三次

为什么要三次握手

  1. 防止失效的连接请求报文段被服务端接收,从而产生错误。
  2. 若建立连接只需两次握手,客户端并没有太大的变化,仍然需要获得服务端的应答后才进入ESTABLISHED状态,而服务端在收到连接请求后就进入ESTABLISHED状态。
  3. 此时如果网络拥塞,客户端发送的连接请求迟迟到不了服务端,客户端便超时重发请求,如果服务端正确接收并确认应答,双方便开始通信,通信结束后释放连接。
  4. 此时,如果那个失效的连接请求抵达了服务端,由于只有两次握手,服务端收到请求就会进入ESTABLISHED状态,等待发送数据或主动发送数据。但此时的客户端早已进入CLOSED状态,服务端将会一直等待下去,这样浪费服务端连接资源。

三次握手的目的是建立可靠的通信信道,说到通讯,简单来说就是数据的发送与接收,而三次握手最主要的目的就是双方确认自己与对方的发送与接收是正常的。

第一次握手:Client 什么都不能确认;Server 确认了对方发送正常,自己接收正常

第二次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:对方发送正常,自己接收正常

第三次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:自己发送、接收正常,对方发送、接收正常(server确认了自己发送也正常)

所以三次握手就能确认双发收发功能都正常,缺一不可。

1.5 四次挥手

能说下四次挥手的流程吗
数据传输结束之后需要断开连接,与建立连接不同,断开连接需要多一次手,四次挥手
在这里插入图片描述

1.5.1 第一次挥手

若A认为数据发送完成,则它需要向B发送连接释放请求。该请求只有报文头,头中携带的主要参数为:FIN=1,seq=u。此时,A将进入FIN-WAIT-1状态

  1. 浏览器向目的主机发出连接结束报文,此时进入FIN WAIT状态;
  2. 连接结束报文标志位FIN=1,并且产生序号 u
  3. TCP连接结束请求报文通过ip->Mac(arp)->网关->目的主机
1.5.2 第二次挥手

B收到连接释放请求后,会通知相应的应用程序,告诉它A向B这个方向的连接已经释放。此时B进入CLOSE-WAIT状态,并向A发送连接释放的应答,其报文头包含:ACK=1,seq=v,ack=u+1

  1. 目的主机接收到数据帧,通过ip->tcp,通过tcp协议单元回应结束应答报文
  2. 结束应答报文中ack = u + 1,表示收到结束请求,当前只是进行回应,因为目的主机可能还有数据
    要传,并不急着断开连接。
    第二次挥手完成后,A到B方向的连接已经释放,B不会再接收数据,A也不会再发送数据。但B到A方向
    的连接仍然存在,B可以继续向A发送数据。
1.5.3 第三次挥手

当B向A发完所有数据后,向A发送连接释放请求,请求头:FIN=1,ACK=1,seq=w,ack=u+1。 B便进入LAST-ACK状态。

  1. 等到浏览器发送完所有数据后,目的主机向我的主机发出tcp连接结束请求报文;
  2. 该报文FIN标志位1,并且产生随机数w,表示结束请求
  3. tcp结束请求报文通过ip->Mac(arp)->网关->我的主机
1.5.4 第四次挥手

A收到释放请求后,向B发送确认应答,此时A进入TIME-WAIT状态。该状态会持续2MSL时间,若该时间段内没有B的重发请求的话,就进入CLOSED状态,撤销TCB。当B收到确认应答后,也便进入CLOSED状态,撤销TCB。

  1. 我的主机收到数据帧,通过ip->tcp,tcp协议单元回应结束应答报文,此时进入TIME WAIT状态,因为不相信网络是可靠的,如果目的主机没收到,还能够重发结束应答报文
  2. 该回应结束应答报文中的FIN标志为1,ack=N+1;表示结束应答,该tcp报文通过ip->Mac(arp)->网关->目的主机;目的主机关闭连接,如果TIME WAIT等待结束后,没有收到回复,说明目的主机连接正常关闭了,我的主机也关闭连接

1.6 为什么是四次挥手

为什么断开连接需要四次挥手而不是两次

因为建立连接时,目的主机可以直接发送SYN+ACK应答报文,而当目的主机收到FIN后,可能还有数据
要发,并不一定直接断开,所以先发送一次应答,告知我的主机收到了连接结束请求。等确认所有数据
都发完了,在发送FIN,同时等待我的主机应答,这里的FIN和ACK不能一起发送,因为可能还有数据要
传输,所以需要四次

1.7 为什么关闭连接需要等待2MSL

为什么从TIME_WAIT到CLOSE需要等待2MSL

  1. 在Client发送出最后的ACK回复,但该ACK可能丢失。Server如果没有收到ACK,将不断重复发送FIN片段。所以Client不能立即关闭,它必须确认Server接收到了该ACK。
  2. Client会在发送出ACK之后进入到TIME_WAIT状态。Client会设置一个计时器,等待2MSL的时间。
    如果在该时间内再次收到FIN,那么Client会重发ACK并再次等待2MSL。
  3. 所谓的2MSL是两倍的MSL(Maximum Segment Lifetime)。MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。

1.8 说下TCP粘包半包

说一下 TCP 粘包半包产生的原因以及解决方案

1.8.1 粘包原因

发送方产生粘包
采用TCP协议传输数据的客户端与服务器经常是保持一个长连接的状态(一次连接发一次数据不存在粘
包),双方在连接不断开的情况下,可以一直传输数据;但当发送的数据包过于的小时,那么TCP协议
默认的会启用Nagle算法,将这些较小的数据包进行合并发送(缓冲区数据发送是一个堆压的过程)

这个合并过程就是在发送缓冲区中进行的,也就是说数据发送出来它已经是粘包的状态了;
接收方产生粘包
接收方采用TCP协议接收数据时的过程是这样的:数据到底接收方,从网络模型的下方传递至传输层,
传输层的TCP协议处理是将其放置接收缓冲区,然后由应用层来主动获取(C语言用recv、read等函
数);这时会出现一个问题,就是我们在程序中调用的读取数据函数不能及时的把缓冲区中的数据拿出
来,而下一个数据又到来并有一部分放入的缓冲区末尾,等我们读取数据时就是一个粘包
;(放数据的
速度 > 应用层拿数据速度)

1.8.2 半包原因

可能是IP分片传输导致的,也可能是传输过程中丢失部分包导致出现的半包,还有可能就是一个包可能被分成了两次传输,在取数据的时候,先取到了一部分(还可能与接收的缓冲区大小有关系),总之就
一个数据包被分成了多次接收

1.8.3 解决粘包半包问题

由于底层的TCP无法理解上层的业务数据,所以在底层是无法保证数据包不被拆分和重组的,这个问题只能通过上层的应用协议栈设计来解决,根据业界的主流协议的解决方案,可以归纳如下。
客户端解决粘包半包

  1. 发送产生是因为Nagle算法合并小数据包,那么可以禁用掉该算法;
  2. TCP提供了强制数据立即传送的操作指令push,当填入数据后调用操作指令就可以立即将数据发送,而不必等待发送缓冲区填充自动发送;
  3. 数据包中加头,头部信息为整个数据的长度(最广泛最常用)
    服务端解决粘包半包
  4. 解析数据包头部信息,根据长度来接收;
  5. 自定义数据格式:在数据中放入开始、结束标识;解析时根据格式抓取数据,缺点是数据内不能含
    有开始或结束标识;

2. http和https的区别

在这里插入图片描述
HTTP:是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP),用于从WWW服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高效,使网络传输减少。
HTTPS:是以安全为目标的HTTP通道,简单讲是HTTP的安全版,即HTTP下加入SSL层,HTTPS的安全
基础是SSL,因此加密的详细内容就需要SSL。
HTTPS协议的主要作用可以分为两种:一种是建立一个信息安全通道,来保证数据传输的安全;另一种
就是确认网站的真实性

2.1 https的通讯流程吗

能说下https的通讯以及加密流程吗
HTTPS能够加密信息,以免敏感信息被第三方获取,所以很多银行网站或电子邮箱等等安全级别较高的
服务都会采用HTTPS协议。
在这里插入图片描述
客户端在使用HTTPS方式与Web服务器通信时有以下几个步骤,如图所示。
在这里插入图片描述

  1. 客户使用https的URL访问Web服务器,要求与Web服务器建立SSL连接。
  2. Web服务器收到客户端请求后,会将网站的证书信息(证书中包含公钥)传送一份给客户端。
  3. 客户端的浏览器与Web服务器开始协商SSL连接的安全等级,也就是信息加密的等级。
  4. 客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,
    并传送给网站。
  5. Web服务器利用自己的私钥解密出会话密钥。
  6. Web服务器利用会话密钥加密与客户端之间的通信。

4. 什么是RPC通讯

RPC(Remote Promote Call) 一种进程间通信方式,允许像调用本地服务一样调用远程服务,RPC框架的主要目标就是让远程服务调用更简单、透明
RPC框架负责屏蔽底层的传输方式(TCP或者UDP)、序列化方式(XML/JSON/二进制)和通信细节。
开发人员在使用的时候只需要了解谁在什么位置提供了什么样的远程服务接口即可,并不需要关心底层
通信细节和调用过程。

在这里插入图片描述

4.1 RPC和HTTP的区别

RPC和HTTTP不是一个并行的概念,RPC是远程过程调用,针对于业务应用来说的,而Http只是一
个网络协议,RPC可以采用http协议来实现,也可以通过更底层的TCP或者socket来实现

RPC,是远程方法调用,只要是调外部的方法,都算。 至于怎么调用,有很多实现。HTTP+json和
dubbo只是实现方式的两种而已。

4.2 RPC和RMI的区别

RMI:Java远程方法调用,即Java RMI(Java Remote Method Invocation)是Java编程语言里, 一种用
实现远程过程调用的应用程序编程接口。它使客户机上运行的程序可以调用远程服务器上的对象,
RMI 只能在 Java 语言中使用, 可以把 RMI 看作面向对象的 Java RPC 。

RPC(Remote Procedure Call Protocol)远程过程调用协议,通过网络从远程计算机上请求调用某种服务。

两者区别如下:

  • RPC 跨语言,而 RMI只支持Java
  • RMI 调用远程对象方法,允许方法返回 Java 对象以及基本数据类型,而RPC 不支持对象的概念

4.3 自己设计一个RPC

能够描述下如何设计一个RPC框架,并描述下每一个组件的作用
我:
服务层
服务层Service,其中主要部分就是动态代理,主要用于将服务提供者的接口通过动态代理将我们的直接方法调用变为网络请求调用
过滤器层
主要完成过滤器链的嵌入,在动态代理阶段做一些事情,比如限流,负载均衡,失败通知,服务监控等等
RPC层
RPC层主要用来屏蔽远程调用的细节,对上层调用透明,也是 RPC 框架的核心部分,包括通信框
架,序列化框架,还有用于屏蔽底层通信框架和序列化框架的抽象接口。
其他组件
服务注册中心
负责服务的发布和通知,通常支持对等集群部署,某个节点宕机不会影响整个集群不可用。即使全部宕
机,只影响新的节点注册和发布,不影响现有的,因为客户端需要缓存服务路由信息。
服务治理中心
服务治理中心通常包括服务治理接口和服务治理 Portal,架构师,测试人员和系统运维人员通过服务治理 Portal 对服务的运行状态,历史数据,健康度和调用关系等进行可视化的分析和维护,目标是要持续优化服务,防止服务架构腐化,保证服务高质量运行

4.3.5 说一下RPC请求过程
  1. 首先服务的提供方和调用方启动的时候会将服务地址注册到注册中心中,然后服务也会拉取对应的
    注册中心的注册信息
  2. 我们调用接口的时候,服务会将调用方的接口通过动态代理从代理到远程接口。
  3. 通过过滤器链从本地存根或者注册中心中获取到调用的服务器列表,然后进行负载均衡筛选出来一
    个调用地址
  4. 然后通过统一的远程调用接口进行网络调用
  5. 先对请求的参数进行序列化
  6. 通过netty或者http对远程地址进行调用,参数是序列化后的地址
  7. 服务提供方收到请求后,对参数进行反序列化然后根据调用地址找到需要调用的目标方法
  8. 调用目标方法将参数传入后调用方法返回接口再将返回的结果进行序列化
  9. 通过网络请求将结果返回给调用发起方,然后将参数进行反序列化
  10. 最终将结束输出

5. Linux IO模型有几种分别是什么

说说常见的IO模型有几种,以及他们之间的区别
阻塞IO、非阻塞IO、多路复用IO、信号驱动IO以及异步IO

5.1 常见的IO模型

5.1.1 阻塞IO

我们钓鱼的时候,有一种方式比较惬意,比较轻松,那就是我们坐在鱼竿面前,这个过程中我们什么也不做,双手一直把着鱼竿,就静静的等着鱼儿咬钩。一旦手上感受到鱼的力道,就把鱼钓起来放入鱼篓中。然后再钓下一条鱼。

最传统的一种IO模型,即在读写数据过程中会发生阻塞现象。

在这里插入图片描述
当用户线程发出IO请求之后,内核会去查看数据是否就绪,如果没有就绪就会等待数据就绪,而用户线
程就会处于阻塞状态,用户线程交出CPU。当数据就绪之后,内核会将数据拷贝到用户线程,并返回结
果给用户线程,用户线程才解除block状态。

data = socket.read();

如果数据没有就绪,就会一直阻塞在read方法。
优点:程序简单,在阻塞等待数据期间进程/线程挂起,基本不会占用 CPU 资源。
缺点:每个连接需要独立的进程/线程单独处理,当并发请求量大时为了维护程序,内存、线程切换开
销较大,这种模型在实际生产中很少使用。

5.1.2 非阻塞IO(轮询)

我们钓鱼的时候,在等待鱼儿咬钩的过程中,我们可以做点别的事情,比如玩一把王者荣耀、看一集《延禧攻略》等等。但是,我们要时不时的去看一下鱼竿,一旦发现有鱼儿上钩了,就把鱼钓上来。

当用户线程发起一个read操作后,并不需要等待,而是马上就得到了一个结果。
在这里插入图片描述
如果结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作。一旦内核中的数据准备好了,并且又再次收到了用户线程的请求,那么它马上就将数据拷贝到了用户线程,然后返回。
所以事实上,在非阻塞IO模型中,用户线程需要不断地询问内核数据是否就绪,也就说非阻塞IO不会交出CPU,而会一直占用CPU。

while(true){ 
   data = socket.read(); 
   if(data!= error){ 
       处理数据 
       break; 
    } 
}

但是对于非阻塞IO就有一个非常严重的问题,在while循环中需要不断地去询问内核数据是否就绪,这样会导致CPU占用率非常高,因此一般情况下很少使用while循环这种方式来读取数据。
优点:不会阻塞在内核的等待数据过程,每次发起的 I/O 请求可以立即返回,不用阻塞等待,实时性较
好。
缺点:轮询将会不断地询问内核,这将占用大量的 CPU 时间,系统资源利用率较低,所以一般 Web 服
务器不使用这种 I/O 模型。

5.1.3 多路复用IO模型

我们钓鱼的时候,为了保证可以最短的时间钓到最多的鱼,我们同一时间摆放多个鱼竿,同时钓鱼。然后哪个鱼竿有鱼儿咬钩了,我们就把哪个鱼竿上面的鱼钓起来。

多路复用IO模型是目前使用得比较多的模型,Java NIO实际上就是多路复用IO。

在这里插入图片描述
在多路复用IO模型中,会有一个线程不断去轮询多个socket的状态,只有当socket真正有读写事件时,才真正调用实际的IO读写操作。因为在多路复用IO模型中,只需要使用一个线程就可以管理多个socket,系统不需要建立新的进程或者线程,也不必维护这些线程和进程,并且只有在真正有socket读写事件进行时,才会使用IO资源,所以它大大减少了资源占用。
在Java NIO中,是通过selector.select()去查询每个通道是否有到达事件,如果没有事件,则一直阻塞在
那里,因此这种方式会导致用户线程的阻塞。
优点:可以基于一个阻塞对象,同时在多个描述符上等待就绪,而不是使用多个线程(每个文件描述符一
个线程),这样可以大大节省系统资源。
缺点:当连接数较少时效率相比多线程+阻塞 I/O 模型效率较低,可能延迟更大,因为单个连接处理需
要 2 次系统调用,占用时间会有增加。

5.1.4 信号驱动IO模型

我们钓鱼的时候,为了避免自己一遍一遍的去查看鱼竿,我们可以给鱼竿安装一个报警器。当有鱼儿咬钩的时候立刻报警。然后我们再收到报警后,去把鱼钓起来。

在信号驱动IO模型中,当用户线程发起一个IO请求操作,会给对应的socket注册一个信号函数,然后用户线程会继续执行,当内核数据就绪时会发送一个信号给用户线程,用户线程接收到信号之后,便在信号函数中调用IO读写操作来进行实际的IO请求操作
在这里插入图片描述
优点:线程并没有在等待数据时被阻塞,可以提高资源的利用率。
缺点:信号 I/O 在大量 IO 操作时可能会因为信号队列溢出导致没法通知。

实际在调用io读取数据的的时候还是阻塞的

我们把钓鱼过程,可以拆分为两个步骤:1、鱼咬钩(数据准备)。2、把鱼钓起来放进鱼篓里(数据拷贝)。无论以上提到的哪种钓鱼方式,在第二步,都是需要人主动去做的,并不是鱼竿自己完成的。所以,这个钓鱼过程其实还是同步进行的。

5.1.5 异步IO模型

在这里插入图片描述

我们钓鱼的时候,采用一种高科技钓鱼竿,即全自动钓鱼竿。可以自动感应鱼上钩,自动收竿,更厉害的可以自动把鱼放进鱼篓里。然后,通知我们鱼已经钓到了,他就继续去钓下一条鱼去了。

异步IO模型才是最理想的IO模型,在异步IO模型中,当用户线程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从内核的角度,当它收到一个asynchronous read之后,它会立刻返回,说明read请求已经成功发起了,因此不会对用户线程产生任何block。然后,内核会等待数据准备完成,然后将数据拷贝到用户线程,当这一切都完成之后,内核会给用户线程发送一个信号,告诉它read操作完成了。也就说用户线程完全不需要知道实际的整个IO操作是如何进行的,只需要先发起一个请求,当接收内核返回的成功信号时表示IO操作已经完成,可以直接去使用数据了
也就说在异步IO模型中,IO操作的两个阶段都不会阻塞用户线程,这两个阶段都是由内核自动完成,然后发送一个信号告知用户线程操作已完成。用户线程中不需要再次调用IO函数进行具体的读写。这点是和信号驱动模型有所不同的,在信号驱动模型中,当用户线程接收到信号表示数据已经就绪,然后需要用户线程调用IO函数进行实际的读写操作;而在异步IO模型中,收到信号表示IO操作已经完成,不需要再在用户线程中调用iO函数进行实际的读写操作。
优点:异步 I/O 能够充分利用 DMA 特性,让 I/O 操作与计算重叠。
缺点:要实现真正的异步 I/O,操作系统需要做大量的工作。目前 Windows 下通过 IOCP 实现了真正的
异步 I/O。

参考

5.2 什么是同步?什么是异步

主要关注的是结果消息的通信机制
同步:同步的意思就是调用方需要主动等待结果的返回
异步:异步的意思就是不需要主动等待结果的返回,而是通过其他手段比如,状态通知,回调函数等
重点看是否是同一个线程返回

5.3 什么是阻塞?什么是非阻塞?

主要关注的是等待结果返回调用方的状态
阻塞:是指结果返回之前,当前线程被挂起,不做任何事
非阻塞:是指结果在返回之前,线程可以做一些其他事,不会被挂起

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值