【计算机网络】TCP可靠传输的原理

计算机网络之TCP可靠传输

一、可靠传输的工作原理

由于计算机网络是分层的,TCP发送的报文段是交给网络层的IP协议处理的。但是IP只能提供"最大努力服务",也就是说下层的网络提供的是不可靠传输,因此TCP必须采取一些措施保证可靠传输。

当传输过程中分组出现差错(检验和)时,应当让发送方重新传送分组;当网络状况不好或者接收方来不及接收分组时,应当适当降低发送速率(流量控制和拥塞控制)。

1.1 停止等待协议

简单的停止等待协议,即发送方每发送一个分组就等待确认,收到接收方的确认后在发送下一个分组。

停止等待协议


当传输的分组出现差错,接收方就丢弃这个分组。或者分组在传输过程中丢失了,接收方同样丢弃这个分组。发送方在每发送一个分组时需要设置一个超时计时器。如果经过一段时间还没有收到确认,发送方就认为分组丢失了,于是自动重传分组。如果在超时时间收到了确认,那么就撤销计时器。

发送方在发送分组后,必须要保留分组的副本(便于重传);以及必须对分组进行编号,这样才能明确发送的是哪一个分组,确认又是对哪个分组的确认。

如果传输的分组是正确的,接收方就向发送方发送确认。如果确认在传输过程中丢失了,或者迟到了,这时由于发送方没有在超时时间内收到确认,会进行重传。接收方在收到重复的分组需要采取的行动,丢弃分组但需要"重传确认"。而如果确认在超时时间后到达了发送方,也就是确认迟到了,那么发送方就可能收到多个确认,这时发送方就简单的丢弃重复的确认。

在这里插入图片描述


从发送方和接收方分别观察一下。发送方执行的动作:收到确认时发送下一个分组、超时重传分组、收到重复确认就简单丢弃。接收方执行的动作:接收分组错误就简单丢弃、接收分组正确并发送确认、收到重复分组就丢弃分组但发送确认。

通常发送方总是可以收到对所有分组的确认。如果发送方迟迟收不到确认,那就说明通信链路出现了问题。
上述的传输机制也称为自动重传请求(ARQ, Automatic Repeat reQuest)。发送方自动重传分组,而不需要接收方请求重传。

停止等待协议的优点是简单,但缺点是信道利用率太低。假定发送方发送分组的时间为Ts,接收方发送确认的时间为Tr,一个分组的往返时间为RTT。那么信道利用率为Ts/(Ts+RTT+Tr)。

为了提高效率,发送方可以采用流水线方式连续发送分组,这样就可以提高信道利用率。

1.2 连续ARQ协议

简单的滑动窗口协议。发送方维持一个发送窗口,发送窗口里的分组可以连续发送出去,而不需要等待确认。

在这里插入图片描述


每收到一个确认时,就将发送窗口向前移动。而接收方一般采用累积确认的方式,不必对每个分组都发送确认,而是对按序到达的最后一个分组发送确认,这样就表示这个分组和之前的分组已经全部收到了。累积确认的优点是容易实现,减少了接收方发送确认的数量。但缺点是不能正确反映已经收到的所有分组的信息。如果接受方按序收到1,2,4,5四个分组,那么只能对2进行确认。发送方不知道后面三个分组的下落,只好重传,也就是Go-back-N,需要回退回来重传这N个分组。当通信线路不好时,连续ARQ协议会带来负面效果,使传输效率降低。

二、TCP报文段的首部格式

TCP的全部功能都表现在报文段的首部中。

在这里插入图片描述


TCP首部最大长度是60字节,其中有20字节固定的首部,以及40字节的可选的选项。图示按照4字节为单位。

2.1 TCP报文段的固定首部

固定首部占20字节。

源端口和目的端口:各占两字节。和UDP的分用相似,TCP分用也是通过端口实现。

序号:占4字节,32位。序号范围是[0,232-1],一共232=4294967296个序号。当序号达到最大值时,下一个序号又回到0也就是说序号采用mod232运算。TCP是面向字节流的,序号也是对字节的编号。序号指的是本报文段第一个字节的编号。如果报文段序号是301,携带的数据100字节,那么下一个报文段序号就是401。

确认号:占4字节。期望收到的下一个字节的序号。例如上面的场景,接收方收到了301-400字节共100个字节,确认号应该为401,即下一个报文段的序号应该是401。如果确认号=N,那么表示到序号N-1为止的所有字节已经正确收到。

数据偏移:占4位。指出TCP报文段的数据部分距离起始位置的字节数,实际上是指出首部的长度。有了首部长度就可以知道选项部分的长度。但数据偏移的单位是4字节,即首部最大长度=15*4=60Byte。

保留:占6位,置为0.

6个控制位

  • 紧急(URG):当URG=1时,表示紧急指针字段有效,即报文段中有紧急数据,需要尽快传送。于是发送方TCP就把紧急数据放到报文段的前面,需要与紧急指针配合使用。
  • 确认(ACK):当ACK=1时,表示确认号字段有效。TCP规定连接建立后所有报文段都要置ACK=1。
  • 推送(PSH):交互式通信时,有时希望在一端键入一个命令后立即收到对方的响应,就可以使用推送。发送方把PSH置1,并立即创建一个报文段发送出去。接收方收到PSH=1的报文段,就尽快的交付接收进程,而不是等到缓存满了才交付。
  • 复位(RST):当RST=1时,表示TCP连接出现严重差错,必须释放连接,然后再重新建立运输连接。还可以用来拒绝打开一个连接。
  • 同步(SYN):当SYN=1时,表示是一个连接请求报文或者连接接受报文段。当SYN=1&ACK=0时,表示是连接请求报文;当SYN=1&ACK=1时,表示是连接接受报文。
  • 终止(FIN):当FIN=1时,表示发送方数据发送完毕,要求释放连接。

窗口:占2字节。窗口值表示的是发送报文的一方的接收窗口,即接收方目前允许对方发送的数据量。窗口作为发送方设置发送窗口的一个依据。

检验和:占2字节。计算检验和和UDP一样,加上12字节的伪首部,进行二进制反码求和。

紧急指针:占2字节,表示紧急数据的字节数。

2.2 TCP报文段的选项和填充

选项长度可变,最大40字节。填充字段将首部填充到4字节的整数倍。

最大报文段长度(MSS,Maximum Segment Size):MSS是TCP报文段数据部分的最大长度。MSS和接收窗口值没有关系。较小的MSS会使网络利用率降低,即数据部分占整个报文段的比例比较低,传送有效数据的能力降低;较大的MSS可能在IP层分解成多个短数据报片。在终点要把多个短数据报片组装成原来的TCP报文段,这些都会使开销增大。因此MSS应该尽可能大些,只要不在IP层分片即可。但由于IP数据报经历的路径是动态变化的,因此最佳的MSS是很难确定的。默认MSS=536,即互联网上的主机都应能接受的报文段长度=536+20=556字节。

窗口扩大选项:占3字节,用于扩大窗口。TCP固定首部中的窗口占2字节,即16位,最大窗口大小=64KB。窗口扩大选项有一个字节表示移位值S,允许使用的最大值是14.
新的窗口位数从16增大到16+S。即加上窗口扩大选项后最大的窗口值大小为2(16+14)=230=1GB。当S=0时,窗口大小回到16位。

时间戳:占10字节,其中时间戳字段4字节,时间戳回送回答字段4字节,用于计算往返时间RTT。发送方发送报文段时把当前时间戳放到时间戳字段,接收方确认时把时间戳字段值复制到时间戳回送回答字段,发送方收到确认后就可以计算RTT。此外还用于处理序号绕回。由于序号只有32位,即232=4294967296B,当使用高速网络时更容易出现序号绕回现象,使用时间戳字段来区分新的报文段和旧的报文段。

三、TCP可靠传输的实现

3.1 以字节为单位的滑动窗口

TCP的滑动窗口是以字节为单位的。包括发送窗口和接收窗口。

发送窗口表示,在没有收到确认的情况下,窗口内的序号可以连续发送;在收到确认之前,所有数据都应保留副本以备重传。

在这里插入图片描述


发送窗口越大,发送方就可以连续发送更多的数据,就可能获得更高的传输效率。但是发送窗口大小不能超过接收方在确认报文中返回的接收窗口的大小(超过了接收方的接受能力),也不能超过拥塞窗口的大小(超过了网络的承受能力)。

发送窗口由前沿和后沿的位置共同确定。后沿的后面部分表示已发送并且已收到确认。前沿的前面部分是不允许发送的数据。后沿的变化有两种情况,即不动和前移,收到确认前不动,收到确认时前移;后沿是不可能后移的。而前沿的变化通常是不断向前移动,但也有可能不动。不动的情形包括两种情况,即没有收到确认,或者收到确认但是接收方返回的窗口变小了。前沿也可能向后收缩,但是TCP标准不建议这样做。

描述一个发送窗口的状态需要三个指针,即P1,P2,P3。小于P1的部分表示已经发送且已经确认,P1~P2之间的部分表示已经发送还未收到确认,P2~p3的部分表示允许发送但还没有发送,大于P3的部分表示不允许发送。

在这里插入图片描述


再来看接收窗口。接收窗口内的序号都允许接收,按序接收、发送确认并交付主机后,就可以丢弃数据。

在这里插入图片描述


只有按序接收数据、确认并交付主机后,接收窗口才可以向前滑动。如果不是按序收到的数据,只能先暂存在窗口中。

如果发送方在一段时间内没有收到确认,那么就要对这些数据进行重传,重设超时计时器。直到收到确认为止。



发送缓存和接收缓存

TCP除了发送窗口和接收窗口,还有发送缓存和接收缓存。发送方的应用进程把字节流写入TCP的发送缓存,接收方的应用进程从TCP的接收缓存中读取字节流。

在这里插入图片描述


发送缓存用来存放:发送应用程序传送给TCP准备发送的数据;已经发送但是还未收到确认的数据。接收缓存用来存放:按序到达的但未被应用程序读取的数据;未按序到达的数据。

如果收到的分组有差错,接收方就会丢弃分组。如果接收应用程序来不及读取数据,接收缓存就会被填满,使窗口减小到0;如果接收应用程序能及时从接收缓存中读取数据,接收窗口就可以增大。但最大不能超过接收缓存的大小。

  • 虽然发送窗口是根据接收方的接收窗口设置的,但是同一时刻两个窗口大小并不总是一样大,因为通过网络传送窗口只需要一定的时间滞后。此外,发送方还可能根据网络拥塞情况调整窗口大小。
  • 对于不按序到达的数据,TCP并无明确规定。如果接收方直接丢弃,那么对网络的利用不利。因此TCP通常会临时存储不按序到达的数据,等到缺少的字节收到后,再按序交付上层应用程序。
  • TCP要求接收方必须有累计确认功能。接收方不应过度推迟确认,TCP规定确认推迟的时间不超过0.5秒。如果收到一连串具有最大长度的报文段,那么必须每隔一个报文段就发送一个确认。

3.2 超时重传时间的选择

发送方发送分组后会设置超时计时器,超过时间则会进行重传。超时重传时间(RTO,Retransmission Time-Out)如果取得过大,则会降低传输效率;取得过小则会引起不必要的重传。TCP采用自适应算法来动态的更新超时重传时间,即根据当前网络的状况不断修正超时重传时间。一个报文段从发出到收到确认所经历的时间,就是报文段的往返时间RTT。TCP保留了加权平均往返时间RTTs和加权平均往返时间偏差RTTd。第一次测量到RTT时,RTTs就取测量到的RTT样本;此后RTTs的值根据公式更新:

RTTs=(1-α)*RTTs+α*RTT

TCP建议的α=1/8=0.125。用这种方法得到的加权平均往返时间就更加平滑。

超时计时器的超时重传时间应该略大于上面的RTTs。因此用这个公式来计算RTO:

RTO=RTTs+4×RTTd

当第一次测量时,RTTd=1/2*RTT。后续RTTd的值根据公式更新:

RTTd=(1-β)*RTTd+β*|RTTs-RTT|

TCP建议的β=1/8=0.125。

对于往返时间的测量,正常情况下可以通过首部选项中的时间戳来完成。但如果发生了超时重传,那么如何确定接收方发来的确认是对原报文的确认,还是对重传报文的确认。由于发送方无法判断,因此计算的RTTs值可能出错。

根据Karn提出的算法,计算加权平均往返时间RTTs时,如果出现了超时重传,那么就不计算往返时间。这样计算的往返时间就比较准确。但在超时重传情况下,如果网络确实变差了,那么超时重传时间实际上应该提高(多留一些时间用来接收确认)。因此修正Karn算法,在每次重传时,将超时重传时间设置为2倍大小。当不再发生超时重传时,再根据上述公式重新计算。

3.3 选择确认SACK

选择确认可以通过TCP首部的选项控制。当收到的报文无差错,只是未按序到达时,可以通过选择确认通知发送方不必发送这些重复数据。

在这里插入图片描述


如图,由于接收的字节块不连续,使用选择确认来通知发送方。这时需要标记不连续字节块的边界,即字节块的左边界序号和右边界序号。

要使用选择确认,需要在建立TCP连接时,在选项字段加上允许SACK的选项。由于首部选项长度最大40字节,而指明一个边界就需要4字节。指明使用选择确认还需要两个字节,一个字节说明是SACK选项,另一个字节说明SACK占用多少字节。因此一个TCP报文段使用选择确认最多报告四个不连续字节块的信息。

四、TCP流量控制

4.1 利用滑动窗口实现流量控制

流量控制是端到端的控制,即发送方和接收方之间的流量控制,主要通过接收方返回的接收窗口来控制。如果发送方发送的速率过快,以至于接收方来不及接收(缓存满了,接收应用程序来不及接收),那么就需要对发送方发送的速率进行控制。

在这里插入图片描述


在连接建立时,接收方在首部的窗口字段中通知发送方,接收窗口rwnd的大小。发送方的发送窗口不能超过接收方接收窗口的大小。随后每一次接收方确认时都会把自己的接收窗口告知发送方,因此接收窗口和发送窗口都是可变的。但如果接收方通知的窗口大小为0,即不允许发送方发送。那么当接收方恢复接收能力的时候,发送方并不知道,也就不会传输数据,这条连接就相当于假死(不再传输数据)。因此TCP规定,当发送方收到接收方的零窗口通知时,启动持续计时器(Persistence Timer),计时器到期时就发送一个零窗口探测报文段(仅携带1字节数据)。接收方在确认时就给出了当前的窗口值,如果接收窗口大小变化,发送方也就可以通过这种探测继续发送数据了。

4.2 TCP的传输效率(发送数据和发送确认的时机)

(个人感觉传输效率和流量控制没什么关系;另外这里似乎只是一种理论上的讨论,并不是真正的实现……不过还是先按书上的来)

可以使用不同的机制来控制发送时机。第一种是当缓存中的数据达到最大报文段长度MSS时,就立即创建一个报文段发送出去。第二种是应用程序指明要求发送报文段,即推送操作。第三种是计时器的时间到期就立即发送。但在实际中广泛使用的Nagle算法:发送应用程序把要发送的数据逐个字节写入到TCP的发送缓存中,则发送方就把第一个字节先发送出去,后面的数据先缓存起来,等到收到接收方的确认后再发送下一个报文段。此外,当到达的数据达到报文段的最大长度,或者已经达到发送窗口的一半时,就立即发送一个报文段。这样可以有效提高网络的吞吐量。

另外一个问题是糊涂窗口综合症(silly window syndrome)。如果接收方的缓存已满,应用程序一次只从接收缓存读取一个字节,然后就向发送方发送确认,确认报文段中的窗口大小=1,那么发送方就只在报文段中方1字节的数据,而TCP首部20字节+IP首部20字节却有40字节,这样使有效数据传输效率大大降低。因此接收方应该控制发送确认的时机。可以让接收方等待一段时间,要么接收缓存能够容纳一个最长报文段,要么接收缓存已有一半的空闲空间,这时向发送方发送确认报文通知窗口大小。当然,接收方不应过度推迟确认。

五、TCP拥塞控制

5.1 拥塞控制的一般原理

如果一段时间内,网络内对资源的需求超过了该资源的总量,就会产生拥塞现象。如果网络中有许多资源同时供应不足,网络的性能就会大幅下降。网络内的资源包括:处理机处理的速率、处理机的缓存容量、链路等。然而要解决拥塞问题,单纯的增加资源供给是不一定能解决问题的,而且还可能使网络变坏。这是因为网络拥塞是一个复杂的问题。单纯的增加某处的资源,往往会将瓶颈转移到其他地方。因此拥塞问题的实质是整个系统的各个部分相互匹配,即网络的整体能力和节点的局部能力匹配。

拥塞控制和流量控制是有区别的。流量控制是端对端的问题,是控制发送方和接收方之间传输的流量,而不管网络情况如何。拥塞控制是为了避免大量的数据涌入网络,造成更严重的拥塞,使得网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程,涉及到所有的主机、路由器以及与降低网络性能有关的所有因素。

在这里插入图片描述


如图,横坐标是提供的负载,单位时间内输入到网络中的分组数量。纵坐标是网络的吞吐量,单位时间内输出的分组数目。理想条件下,当负载没有超过网络的最大能力时,吞吐量等于分组(45度斜线),当负载超过了网络的最大能力时,吞吐量就等于网络提供的最大能力。然而实际情况下在网络吞吐量明显的小于理想的吞吐量时,网络久进入了轻度拥塞状态。当提供的负载大到某一数值时,网络的吞吐量反而开始下降,甚至降低到0,使网络无法工作,进入死锁状态(deadlock)。在实际的拥塞控制下,当提供的负载还没有达到网络的最大能力时,拥塞控制就已经开始发挥作用,使得吞吐量无限趋近理想的最大吞吐量。

从控制理论的角度来看,解决拥塞问题有开环控制和闭环控制两种方法。开环控制即设计网络时就将各种因素考虑到,一旦运行起来就不再更改了。闭环控制基于反馈环路的概念,通过检测网络系统的运行,及时采取行动来避免拥塞。用于反应拥塞问题的指标包括:缓存缺少空间丢弃的分组数、平均队列长度、超时重传的分组数、平均分组时延等等。一般监测到拥塞发生时,要把拥塞的信息传送给发送分组的源站。另一种方法是在路由器转发的分组中保留一个比特或字段,来表示网络发生了拥塞。也可以由一些主机或路由器周期性的探测网络情况。

实际上拥塞控制机制是很难设计的,有时候一个不好的拥塞控制机制恰恰会引发网络的拥塞。

5.2 TCP的拥塞控制方法

TCP进行拥塞控制的算法有四种,慢开始(slow-start)、拥塞避免(congestion avoidance)、快重传(fast retransmit)和快恢复(fast recovery)。

基于窗口的拥塞控制,发送方维持一个拥塞窗口cwnd(congestion window)。拥塞窗口的大小随网络拥塞程度变化而变化,发送方根据拥塞窗口设置发送窗口。只要网络没有发生拥塞,拥塞窗口就可以增大一些;但只要网络出现拥塞,或者可能出现拥塞,拥塞窗口就必须减小。通信线路的传输质量一般都很好,因为传输出现差错而丢弃分组的概率是很小的。因此发送方判断网络出现拥塞的依据就是超时。上面提到的四种算法都是用于拥塞情况下调整拥塞窗口以及快速处理可能引起误判的3-ACK分组。

慢开始算法:当主机开始发送数据时,由于不清楚当前的网络情况,因此不应该立即向网络注入大量数据,所以应该从小到大增大拥塞窗口数值,通过慢开始探测网络情况。新的RFC5681把初始的cwnd设置为不超过2~4个SMSS(发送方最大报文段长度,SMSS, Sender Maximum Segment Size)。注意,拥塞窗口的单位是字节。慢开始算法规定,每收到一个报文段的确认后,可以把拥塞窗口增加最多一个SMSS的数值。cwnd增加量=MIN(N,SMSS),N为本次确认的字节数。下面以报文段个数作为窗口大小的单位,即初始值为cwnd=1,以后每收到一个报文段的确认就增加1。

在这里插入图片描述

使用慢开始算法,每经过一个传输轮次(transmission round),拥塞窗口就加倍,因此是指数增长。传输轮次是指将拥塞窗口允许发送的报文段全部发送,并收到对最后一个字节的确认。在实际情况下,发送方只要收到一个报文段的确认,就可以立即发送下一个报文段,而不必等待传输轮次都传输完。

为了防止慢开始算法使拥塞窗口增大过快,设置一个慢开始门限(ssthreshold,slow-start threshold)。(如何设置慢开始门限?)慢开始门限表示:当cwnd<ssthreshold时,就使用慢开始算法;当cwnd>ssthreshold时,改用拥塞避免算法。

拥塞避免算法:让拥塞窗口缓慢的增大,即每经过一个传输轮次RTT,拥塞窗口cwnd就加1,而不是像慢开始算法加倍扩大。因此拥塞避免阶段有"加法扩大"(Additive Increase)。这表明在拥塞避免阶段,拥塞窗口按照线性规律增长。

在这里插入图片描述

如图所示。拥塞避免阶段中cwnd=24时,出现了超时重传,发送方判断可能发生了网络拥塞,于是将cwnd置1,门限值ssthreshold=cwnd/2=12,再次进行慢开始算法。当拥塞窗口cwnd=12时,又改为拥塞避免算法。经过一段时间的增长,cwnd=16时,出现了3-ACK(发送方连续三次收到对同一分组的确认)。3-ACK表示某一分组传输过程中很可能丢失了,导致接收方没有收到,而实际上网络可能没有发生拥塞,但由于发生超时,发送方就会主动降低发送速率。因此要采用快重传和快恢复。

快重传算法:快重传可以让发送方尽早知道丢失的分组。快重传算法要求接收方收到分组时要立即发送确认,即使收到了失序的报文段也必须立即确认。这样发送方一旦发现某一分组出现3-ACK现象,说明该分组在传输过程中丢失了,应该立即重传。如果在超时时间前收到了接收方的确认,那么就可以避免重新进入慢开始。快重传可以使网络的吞吐量提高20%。因此当cwnd=16时,发送方知道有分组丢失了,于是不启动慢开始,而是执行快恢复算法。

快恢复算法:这时ssthreshold=cwnd/2=8,cwnd=ssthreshold=8,并开始执行拥塞避免算法。(这块书有点坑啊,注释说了门限值减半是简化的说法,实际上TCP的计算公式ssthreshold=max(FlightSize/2, 2*SMSS), 其中FlightSize就是正在网络中传送的数据量,即已发送还未收到确认的数据)
在拥塞避免阶段,拥塞窗口是线性增大的,常称为加法增大(Additive Increase)。而一旦出现超时或者3-ACK,就要把门限值设置为当前窗口的一半,常称为乘法减小(Multiplicative Decrease)。二者合在一起就是所谓的AIMD算法。

采用这样的拥塞控制方法使得TCP性能得到明显改进。

在这里插入图片描述


如图为TCP拥塞控制整个的流程图。不管当前处于什么阶段,只要发生重传,就将门限值减半、拥塞窗口置1,并执行慢开始算法;只要出现3—ACK,就将门限值减半,拥塞窗口置为门限值大小。

考虑到TCP首部中的接收窗口,发送方的发送窗口大小需要受到接收窗口和拥塞窗口的共同制约。即发送方发送窗口的上限=min(rwnd,cwnd)。

5.3 主动队列管理

考虑网络层的策略对拥塞控制的影响。其中影响最大的就是路由器的分组丢弃策略。理想情况下路由器的队列通常按照先进先出(FIFO)规则处理到来的分组;当队列已满时,就丢弃后续到达的分组,称之为"尾部丢弃策略"。路由器的尾部丢弃往往会导致一系列分组的丢失,并使得TCP连接的发送方出现超时重传,突然把传输速率降低到很小。由于网络中有很多的TCP连接, 因此可能导致许多连接同时出现超时重传并降低传输速率,称之为"全局同步"。全局同步使得网络中的数据量突然降低到很小的水平。

为了避免发生网络中的全局同步现象,提出了主动队列管理(AQM,Active Queue Management)。不是在队列已满的时候才丢弃分组,而是在队列长度到达某个值时就开始丢弃分组。AQM的一种实现是随机早期检测(Random Early Detection),或者随机早期丢弃(Random Early Drop)。实现RED需要使路由器维持两个参数,即队列最小长度门限和最大长度门限。当分组到达时,如果平均队列长度小于最小长度门限,就把分组加入队列;如果平均队列长度超过最小长度门限,小于最大长度门限,则以随机概率p丢弃分组; 平均队列长度超过最大长度门限,则丢弃分组。因此RED是在监测到网络拥塞的早期征兆时就采取措施丢弃分组来避免拥塞。但由于随机概率p不易选择,RED的效果也不太理想,因此RED已经不再推荐使用,但替代算法还处于实验阶段。





参考资料:《计算机网络第七版》谢希仁

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值