认识TCP&&三次握手&&四次挥手&&TCP保证可靠的机制

1.TCP基本认识

TCP是面向连接的、可靠的、基于字节流的传输层通信协议。

  • ⾯向连接:⼀定是「⼀对⼀」才能连接,不能像 UDP 协议可以⼀个主机同时向多个主机发送消息,也就是⼀ 对多是⽆法做到的;
  • 可靠的:⽆论的⽹络链路中出现了怎样的链路变化,TCP 都可以保证⼀个报⽂⼀定能够到达接收端
  • 字节流:消息是「没有边界」的,所以⽆论我们消息有多⼤都可以进⾏传输。并且消息是「有序的」,当 「前⼀个」消息没有收到的时候,即使它先收到了后⾯的字节,那么也不能扔给应⽤层去处理,同时对「重复」的报⽂会⾃动丢弃

1.1 TCP头格式

在这里插入图片描述

  • 序列号:在建立连接的时候,计算机会生成一个随机数作为初始值,并通过SYN包发送给服务器,每次发送一次数据,就累加一次该数据字节数的大小。这样服务器就知道接受到的包是否是顺序的。用来解决网络包乱序问题

  • 确认应答号:指下一次想要收到的数据的序列号从当前这个应答号开始,发送端收到这个确认后可以认为在这个包之前的数据都被正常接受。用来解决不丢包的问题

  • 控制位:

    • ACK:建立连接时,将此位置为1.
    • RST:该位为1时,说明TCP连接出现异常,强制断开连接
    • SYN:同步的意思,该位为1时,表示我想和你建立连接。并在其「序列号」的字段进⾏序列号初始值的设定。
    • FIN:说拜拜,当此位为1时,表示我这边没有数据发送了,希望可以断开连接。当通信结束希望断开连接时,通信双方的 主机之间就可以相互交换 FIN 位为 1 的 TCP 段
  • 窗口大小:用来做流量控制

1.2 TCP工作层次

计算机网络模型

在这里插入图片描述

由于IP层是不可靠的,他不保证网络包的交付、不保证网络包的按序交付、也不保证网络包中的数据完整型。如果需要保障网络数据包的可靠性,那么就需要由上层(传输层)的TCP协议来负责。

因为 TCP 是⼀个⼯作在传输层可靠数据传输的服务,它能确保接收端接收的⽹络包是⽆损坏、⽆间隔、⾮冗余 和按序的

1.3 UDP和TCP区别

UDP不提供复杂的控制机制,利⽤ IP 提供⾯向「⽆连接」的通信服务。

在这里插入图片描述

  • ⽬标和源端⼝:主要是告诉 UDP 协议应该把报⽂发给哪个进程。
  • ⽬标和源端⼝:主要是告诉 UDP 协议应该把报⽂发给哪个进程。
  • 校验和:校验和是为了提供可靠的 UDP ⾸部和数据⽽设计。

TCP和UDP的区别:

  1. 连接
    • TCP是面向连接的传输层协议,传输数据前要先建立连接
    • UDP不需要连接,即可传输数据
  2. 服务对象
    • TCP是1对1的两点服务,即一条连接只有两个端点
    • UDP支持一对一、一对多、多对多的交互通信
  3. 可靠性
    • TCP 是可靠交付数据的,数据可以⽆差错、不丢失、不重复、按需到达。
    • UDP 是尽最⼤努⼒交付,不保证可靠交付数据。
  4. 拥塞控制、流量控制
    • TCP 有拥塞控制和流量控制机制,保证数据传输的安全性。
    • UDP 则没有,即使⽹络⾮常拥堵了,也不会影响 UDP 的发送速率
  5. 首部开销
    • TCP ⾸部⻓度较⻓,会有⼀定的开销,⾸部在没有使⽤「选项」字段时是 20 个字节,如果使⽤了「选项」 字段则会变⻓的。
    • UDP ⾸部只有 8 个字节,并且是固定不变的,开销较⼩
  6. 传输方式
    • TCP 是流式传输,没有边界,但保证顺序和可靠
    • UDP 是⼀个包⼀个包的发送,是有边界的,但可能会丢包和乱序
  7. 分⽚不同
    • TCP 的数据⼤⼩如果⼤于 MSS (最大报文段长度)⼤⼩,则会在传输层进⾏分⽚,⽬标主机收到后,也同样在传输层组装 TCP 数据包,如果中途丢失了⼀个分⽚,只需要传输丢失的这个分⽚
    • UDP 的数据⼤⼩如果⼤于 MTU (最大传输单元)⼤⼩,则会在 IP 层进⾏分⽚,⽬标主机收到后,在 IP 层组装完数据,接着 再传给传输层,但是如果中途丢了⼀个分⽚,在实现可靠传输的 UDP 时则就需要重传所有的数据包,这样 传输效率⾮常差,所以通常 UDP 的报⽂应该⼩于 MTU
  8. 应用场景
    • 由于 TCP 是⾯向连接,能保证数据的可靠性交付,因此经常⽤于:FTP文件传输、HTTP/HTTPS
    • 由于 UDP ⾯向⽆连接,它可以随时发送数据,再加上UDP本身的处理既简单⼜⾼效,因此经常⽤于:包总量较少的通信,如 DNS 、 SNMP。视频、音频等

1.4 三次握手

TCP 是⾯向连接的协议,所以使⽤ TCP 前必须先建⽴连接,⽽建⽴连接是通过三次握⼿来进⾏的。

在这里插入图片描述

  • 一开始,客户端和服务都处于CLOSED状态,服务器先开启监听某个端口,处于LISTEN状态。

在这里插入图片描述

  • 客户端随机初始化序号(Clint_isn),将此需要赋值给TCP首部的序列号字段,同时把SYN位置为1,然后发送这个报文,该报文不包含应用层数据,之后客户端处于SYN-SENT状态。SYN就是英语synchronize,表示同步,这个报文就是说:兄弟,我的序列号是x,并且我的syn是1,我现在要和你建立连接了。

在这里插入图片描述

  • 服务器收到客户端发过来的报文后,发现SYN = 1,就说:哦,兄弟你要和我建立连接呀,那我也初始化我的序列号(server—isn)。为了让你知道我收到了,我的确认应答号就等于你的序列号+1吧。我也要和你同步,那我的SYN就要变成1,我要和你建立连接,ACK也变成1吧。我准备好了,我把信息就发给你了哦。该报⽂也不包含应⽤层数据,之后服务器处于SYN-RCVD状态。

在这里插入图片描述

  • 客户端收到服务端报⽂后,说:我收到了,bro。我也记一下你的序列号,把他放到我的确认应答号处,值就位你的序列号+1。咋俩都建立连接了,那我的ACK就为1啦。这次报⽂可以携带客 户到服务器的数据,之后客户端处于 ESTABLISHED 状态。

  • 服务器收到客户端的应答报⽂后,也进⼊ **ESTABLISHED **状态

从上⾯的过程可以发现第三次握⼿是可以携带数据的,前两次握⼿是不可以携带数据的

⼀旦完成三次握⼿,双⽅都处于ESTABLISHED 状态,此时连接就已建⽴完成,客户端和服务端就可以相互发送 数据了。

1.5 为什么需要三次握手

从三个方面来看:

  • 三次握⼿才可以阻止重复历史连接的初始化(主要原因)
  • 三次握⼿才可以同步双⽅的初始序列号
  • 三次握⼿才可以避免资源浪费

1.5.1 原因1:避免历史连接

⽹络环境是错综复杂的,往往并不是如我们期望的⼀样,先发送的数据包,就先到达⽬标主机,反⽽它很骚,可能 会由于⽹络拥堵等乱七⼋糟的原因,会使得旧的数据包,先到达⽬标主机,那么这种情况下 TCP 三次握⼿是如何 避免的呢?

在这里插入图片描述

客户端连续发送多次 SYN 建⽴连接的报⽂,在⽹络拥堵情况下:

  • ⼀个「旧 SYN 报⽂」⽐「最新的 SYN 」 报⽂早到达了服务端;
  • 那么此时服务端就会回⼀个 SYN + ACK 报⽂给客户端;
  • 客户端收到后可以根据⾃身的上下⽂,判断这是⼀个历史连接(序列号过期或超时),那么客户端就会发送 RST 报⽂给服务端,表示中⽌这⼀次连接。

如果是两次握⼿连接,就不能判断当前连接是否是历史连接,三次握⼿则可以在客户端(发送⽅)准备发送第三次 报⽂时,客户端因有⾜够的上下⽂来判断当前连接是否是历史连接。

  • 如果是历史连接(序列号过期或超时),则第三次握⼿发送的报⽂是 RST 报⽂,以此中⽌历史连接;
  • 如果不是历史连接,则第三次发送的报⽂是 ACK 报⽂,通信双⽅就会成功建⽴连接;

1.5.2 原因2:同步双方初始化序列号

TCP 协议的通信双⽅, 都必须维护⼀个「序列号」, 序列号是可靠传输的⼀个关键因素,它的作⽤:

  • 接收⽅可以去除重复的数据
  • 接收⽅可以根据数据包的序列号按序接收;
  • 可以标识发送出去的数据包中, 哪些是已经被对⽅收到的;

可⻅,序列号在 TCP 连接中占据着⾮常重要的作⽤,所以当客户端发送携带「初始序列号」的 SYN 报⽂的时 候,需要服务端回⼀个 ACK 应答报⽂,表示客户端的 SYN 报⽂已被服务端成功接收,那当服务端发送「初始序 列号」给客户端的时候,依然也要得到客户端的应答回应,这样⼀来⼀回,才能确保双⽅的初始序列号能被可靠的 同步。

在这里插入图片描述

四次握手其实也可以做的,但2、3步完全可以合并,所有就成了三次握手。

⽽两次握⼿只保证了⼀⽅的初始序列号能被对⽅成功接收,没办法保证双⽅的初始序列号都能被确认接收

1.5.3 原因3:避免资源浪费

如果只有「两次握⼿」,当客户端的 SYN 请求连接在⽹络中阻塞,客户端没有接收到 ACK 报⽂,就会重新发 送 SYN ,由于没有第三次握⼿,服务器不清楚客户端是否收到了⾃⼰发送的建⽴连接的 ACK 确认信号,所以 每收到⼀个 SYN 就只能先主动建⽴⼀个连接,这会造成什么情况呢?

服务器重复接受⽆⽤的连接请求 SYN 报⽂,⽽造成重复复分配资源。

1.5 四次握手

TCP断开连接的方式就是四次握手。双方都可以主动断开连接,断开连接后主机中的资源被释放。

在这里插入图片描述

  • 客户端打算关闭连接,发送一个TCP首部FIN标志位被置为1的报文,也即FIN报文,之后客户端进入FIN-WAIT_1状态
  • 服务端收到该报⽂后,就向客户端发送 ACK 应答报⽂,接着服务端进⼊ CLOSED_WAIT 状态
  • 客户端收到服务端的 ACK 应答报⽂后,之后进⼊ FIN_WAIT_2 状态。
  • 等待服务端处理完数据后,也向客户端发送 FIN 报⽂,之后服务端进⼊ LAST_ACK 状态。
  • 客户端收到服务端的 FIN 报⽂后,回⼀个 ACK 应答报⽂,之后进⼊ TIME_WAIT 状态
  • 服务器收到了 ACK 应答报⽂后,就进⼊了 CLOSED 状态,⾄此服务端已经完成连接的关闭。
  • 客户端在经过 2MSL ⼀段时间后,⾃动进⼊ CLOSED 状态,⾄此客户端也完成连接的关闭

这里需要注意,谁主动关闭,谁有TIME_WATE状态。

1.6 为什么需要四次挥手

  • 关闭服务是,客户端向服务发送FIN时,仅表示我不不发数据了,但我还可以接受数据
  • 服务器收到客户端的 FIN 报⽂时,先回⼀个 ACK 报⽂,表示我知道了,⽽服务端可能还有数据需要处理和发送,等 服务端不再发送数据时,才发送 FIN 报⽂给客户端来表示同意现在关闭连接。

从上⾯过程可知,服务端通常需要等待完成数据的发送和处理,所以服务端的ACKFIN ⼀般都会分开发 送,从⽽⽐三次握⼿导致多了⼀次。

1.7 为什么TIME_WATE等待时间是2MSL

MSL 是 Maximum Segment Lifetime,报⽂最⼤⽣存时间,它是任何报⽂在⽹络上存在的最⻓时间,超过这个时 间报⽂将被丢弃。因为 TCP 报⽂基于是 IP 协议的,⽽ IP 头中有⼀个 TTL 字段,是 IP 数据报可以经过的最⼤路 由数,每经过⼀个处理他的路由器此值就减 1,当此值为 0 则数据报将被丢弃,同时发送 ICMP 报⽂通知源主机。

2MSL刚好就是发出报文到接收到来回的时间。

⽐如如果被动关闭⽅没有收到断开连接的最后的 ACK 报⽂,就会触发超时触发 Fin 报⽂,另⼀⽅接收到 FIN 后, 会重发ACK 给被动关闭⽅, ⼀来⼀去正好 2 个 MSL。

2MSL 的时间是从客户端接收到 FIN 后发送 ACK 开始计时的。如果在 TIME-WAIT 时间内,因为客户端的 ACK 没有传输到服务端,客户端⼜接收到了服务端重发的 FIN 报⽂,那么 2MSL 时间将重新计时。

1.8 为什么需要TIME_WATE状态

主动发起关闭连接的⼀⽅,才会有 TIME-WAIT 状态。

需要 TIME-WAIT 状态,主要是两个原因:

  • 防⽌具有相同「四元组」(己方ip+port + 对方ip+port)的「旧」数据包被收到;

  • 保证「被动关闭连接」的⼀⽅能被正确的关闭,即保证最后的 ACK 能让被动关闭⽅接收,从⽽帮助其正常关 闭;

1.8.1 原因1:防止旧连接的数据包

假如一个来自服务器在关闭之前发送的报文被网络延迟了。这时候有个相同端口的TCP连接被复用了,被延迟的包抵达了客户端,那么客户端是有可能正常接收 这个过期的报⽂,这就会产⽣数据错乱等严重的问题。

所以,TCP 就设计出了这么⼀个机制,经过 2MSL 这个时间,⾜以让两个⽅向上的数据包都被丢弃,使得原来 连接的数据包在⽹络中都⾃然消失,再出现的数据包⼀定都是新建⽴连接所产⽣的。

原因2:保证连接正确关闭

TIME-WAIT 作⽤是等待⾜够的时间以确保最后的 ACK 能让被动关闭⽅接收,从⽽帮助其正常关闭。(不能让老哥一直等呀)


TIME_WATE过多的危害?

  • 对内存资源的占用
  • 对端口资源的占用,一个TCP连接至少消耗一个本地端口

客户端受端口资源限制:客户端TIME_WAIT过多,就会导致端⼝资源被占⽤,因为端⼝就65536个,被占满就会导致⽆法创建新的连 接。

服务端受系统资源限制:由于⼀个四元组表示 TCP 连接,理论上服务端可以建⽴很多连接,服务端确实只监听⼀个端⼝ 但是会把连接 扔给处理线程,所以理论上监听的端⼝可以继续监听。但是线程池处理不了那么多⼀直不断的连接了。所以 当服务端出现⼤量TIME_WAIT 时,系统资源被占满时,会导致处理不过来新的连接。

2. TCP重传、滑动窗口、流量控制、拥塞控制

2.1 TCP重传

TCP实现可靠性连接的方式之一,就是通过序列号与确认应答。

在 TCP 中,当发送端的数据到达接收主机时,接收端主机会返回⼀个确认应答消息,表示已收到消息。

在这里插入图片描述

但是在复杂的网络当中,数据并不会如你所愿的传输,可能会发生丢失。这里就引出了重传机制

常见的重传机制有:

  • 超时重传
  • 快速重传
  • ASCK
  • D-ASCK

2.1.1 超时重传

重传机制的其中⼀个⽅式,就是在发送数据时,设定⼀个定时器,当超过指定的时间后,没有收到对⽅的 ACK 确认应答报⽂,就会重发该数据,也就是我们常说的超时重传。

TCP发生重传的时机:

  • 数据包丢失
  • 确认应答丢失

在这里插入图片描述

超时时间应该设为多少?

RTT(Round-Trip Time 往返时延) 就是数据从⽹络⼀端传送到另⼀端所需的时间,也就是包的往返时间。

超时重传时间是以 RTO (Retransmission Timeout 超时重传时间)表示。

由此可见,超时重传时间应该略大于往返时延。

如果超时时间设置过短,会导致可能并没有丢就重发,于是重发的就快,会增加⽹络拥塞,导致更多的超 时,更多的超时导致更多的重发。

超时时间设置过长,重发就慢,丢了⽼半天才重发,没有效率,性能差。导致网络的空隙时间增大,降低了网络传输时延。

2.1.2 快速重传

TCP 还有另外⼀种快速重传(Fast Retransmit)机制,它不以时间为驱动,⽽是以数据驱动重传。

在这里插入图片描述

由图可知,三次接受方返回的ACK报文相同,这时候就触发了快速重传。

所以,快速重传的⼯作⽅式是当收到三个相同的 ACK 报⽂时,会在定时器过期之前,重传丢失的报⽂段。

但是,快速重传机制只解决了⼀个问题,就是超时时间的问题,但是它依然⾯临着另外⼀个问题。就是重传的时候,是重 传之前的⼀个,还是重传所有的问题。

2.1.3 ASCK方法

快速重传的一个优化就是SACK ( Selective Acknowledgment 选择性确认)。

这种方式需要在TCP头部[选项]放一个SACK的东西,他可以将缓存的地图发送给发送方,这样发送方就知道哪些包没有传过去了,就只发没有传过去的数据。

在这里插入图片描述

2.1.4 D-ASCK

Duplicate SACK ⼜称 D-SACK ,其主要使⽤了 SACK 来告诉「发送⽅」有哪些数据被重复接收了。

  • ACK丢包

在这里插入图片描述

  • 网络延时

在这里插入图片描述

从上面两个图来看,D-SACK有这个几个好处:

  • 可以让「发送⽅」知道,是发出去的包丢了,还是接收⽅回应的 ACK 包丢了
  • 可以知道是不是「发送⽅」的数据包被⽹络延迟了;
  • 可以知道⽹络中是不是把「发送⽅」的数据包给复制了

2.2 滑动窗口

TCP是每发送一个消息,都要进行一次应答。当上一个数据包收到后,才发下一个。这个效率是不是太低了?

为解决这个问题,TCP 引⼊了窗⼝这个概念。即使在往返时间较⻓的情况下,它也不会降低⽹络通信的效率。那么有了窗⼝,就可以指定窗⼝⼤⼩,窗⼝⼤⼩就是指⽆需等待确认应答,⽽可以继续发送数据的最⼤值,窗⼝的实现实际上是操作系统开辟的⼀个缓存空间,发送⽅主机在等到确认应答返回之前,必须在缓冲区中保留已 发送的数据。如果按期收到确认应答,此时数据就可以从缓存区清除。

窗口大小由哪一方决定?

这里就要看之前的TCP头部结构了,TCP首部存在一个window字段,也就是窗口大小。

这个字段是接收端告诉发送端⾃⼰还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能⼒来 发送数据,⽽不会导致接收端处理不过来。

为滑动窗⼝并不是⼀成不变的。⽐如,当接收⽅的应⽤进程读取数据的速度⾮常快的话,这样的话接收窗⼝可以 很快的就空缺出来。那么新的接收窗⼝⼤⼩,是通过 TCP 报⽂中的 Windows 字段来告诉发送⽅。那么这个传输过 程是存在时延的,所以接收窗⼝和发送窗⼝是约等于的关系。

2.3 流量控制

发送⽅不能⽆脑的发数据给接收⽅,要考虑接收⽅处理能⼒

如果⼀直⽆脑的发数据给对⽅,但对⽅处理不过来,那么就会导致触发重发机制,从⽽导致⽹络流量的⽆端的浪 费。

为了解决这种现象发⽣,TCP 提供⼀种机制可以让「发送⽅」根据「接收⽅」的实际接收能⼒控制发送的数据量, 这就是所谓的流量控制。

2.4 拥塞控制

前⾯的流量控制是避免「发送⽅」的数据填满「接收⽅」的缓存,但是并不知道⽹络的中发⽣了什么。

在⽹络出现拥堵时,如果继续发送⼤量数据包,可能会导致数据包时延、丢失等,这时 TCP 就会重传数据,但是 ⼀重传就会导致⽹络的负担更重,于是会导致更⼤的延迟以及更多的丢包,这个情况就会进⼊恶性循环被不断地放 ⼤…

所以,TCP 不能忽略⽹络上发⽣的事,它被设计成⼀个⽆私的协议,当⽹络发送拥塞时,TCP 会⾃我牺牲,降低 发送的数据量。

于是,就有了拥塞控制,控制的⽬的就是避免「发送⽅」的数据填满整个⽹络

这里就需要定义一个拥塞窗口拥塞窗⼝ cwnd是发送⽅维护的⼀个的状态变量,它会根据⽹络的拥塞程度动态变化的。

我们在前⾯提到过发送窗⼝ swnd 和接收窗⼝ rwnd 是约等于的关系,那么由于加⼊了拥塞窗⼝的概念后,此 时发送窗⼝的值是swnd = min(cwnd, rwnd),也就是拥塞窗⼝和接收窗⼝中的最⼩值。

拥塞窗⼝ cwnd 变化的规则:

  • 只要⽹络中没有出现拥塞, cwnd 就会增⼤;
  • 但⽹络中出现了拥塞, cwnd 就减少

拥塞控制算法:慢启动,拥塞避免,拥塞发生,快速恢复。

2.4.1 慢启动

TCP 在刚建⽴连接完成后,⾸先是有个慢启动的过程,这个慢启动的意思就是⼀点⼀点的提⾼发送数据包的数量, 如果⼀上来就发⼤量的数据,这不是给⽹络添堵吗?(慢慢试探)

慢启动的算法记住⼀个规则就⾏:当发送⽅每收到⼀个 ACK,拥塞窗⼝ cwnd 的⼤⼩就会加 1。慢启动算法,发包的个数是指数性的增⻓。

那么慢启动涨到什么时候呢?

有⼀个叫慢启动⻔限 **ssthresh (slow start threshold)**状态变量。

  • 当 cwnd < ssthresh 时,使⽤慢启动算法
  • cwnd >= ssthresh 时,就会使⽤「拥塞避免算法」

2.4.2 拥塞避免

当拥塞窗⼝ cwnd 「超过」慢启动⻔限 ssthresh 就会进⼊拥塞避免算法。⼀般来说 ssthresh 的⼤⼩是 65535 字节。

那么进⼊拥塞避免算法后,它的规则是:每当收到⼀个 ACK 时,cwnd 增加 1/cwnd。

拥塞避免算法就是将原本慢启动算法的指数增⻓变成了线性增⻓,还是增⻓阶段,但是增⻓ 速度缓慢了⼀些

就这么⼀直增⻓着后,⽹络就会慢慢进⼊了拥塞的状况了,于是就会出现丢包现象,这时就需要对丢失的数据包进行重传。 当触发了重传机制,也就进⼊了「拥塞发⽣算法」。

2.4.3 拥塞发生

当⽹络出现拥塞,也就是会发⽣数据包重传,重传机制主要有两种:超时重传、快速重传。

  • 发⽣超时重传的拥塞发⽣算法

    • ssthresh 设为 cwnd/2

    • cwnd 重置为 1

    • 进入慢启动

  • 发⽣快速重传的拥塞发⽣算法

    • cwnd = cwnd/2 ,也就是设置为原来的⼀半;
    • ssthresh = cwnd ;
    • 进入快速恢复算法

2.4.4 快速恢复

快速重传和快速恢复算法⼀般同时使⽤,快速恢复算法是认为,你还能收到 3 个重复 ACK 说明⽹络也不那么糟 糕,所以没有必要像 RTO 超时那么强烈

进⼊快速恢复算法如下:

  • 拥塞窗⼝ cwnd = ssthresh + 3 ( 3 的意思是确认有 3 个数据包被收到了);
  • 重传丢失的数据包;
  • 如果再收到᯿复的 ACK,那么 cwnd 增加 1;
  • 如果收到新数据的 ACK 后,把 cwnd 设置为第⼀步中的 ssthresh 的值,原因是该 ACK 确认了新的数据,说 明从 duplicated ACK 时的数据都已收到,该恢复过程已经结束,可以回到恢复之前的状态了,也即再次进 ⼊拥塞避免状态

在这里插入图片描述

整个过程如图所示。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值