当前位置: 首页 > news >正文

面向连接的运输:TCP

目录

TCP连接

TCP报文段结构

往返时间估计与超时

可靠数据传输 

回退N步or超时重传

超时间隔加倍

快速重传

流量控制

TCP连接管理 

三次握手

1. 客户端 → 服务器:SYN 包

2. 服务器 → 客户端:SYN+ACK 包

3. 客户端 → 服务器:ACK 包

四次挥手

1. 主动关闭方 → 被动关闭方:FIN 包

2. 被动关闭方 → 主动关闭方:ACK 包

3. 被动关闭方 → 主动关闭方:FIN 包

4. 主动关闭方 → 被动关闭方:ACK 包

 为什么必须要三次握手?

为什么不能用两次握手?

四次或更多次握手可行吗?

为什么要四次挥手?

为什么不能用三次挥手?

两次挥手可行吗?


TCP是因特网运输层面向连接的可靠传输协议。为了提供可靠数据传输,TCP依赖前文可靠数据传输原理 许多基本原理,其中包括差错检测,重传,累积确认,定时器以及用于序号和确认号的首部字段等。

TCP连接

TCP 被称为面向连接的协议,核心在于其通信过程需要经历 “连接建立→数据传输→连接释放” 的完整生命周期。TCP 通信前必须通过三次报文交互,在客户端和服务器之间协商并确认连接参数,确保双方具备通信能力(三次握手)。通信结束后,需通过四次报文交互主动释放连接资源,确保双方无数据残留(四次挥手)。TCP 的 “面向连接” 本质是状态化通信,连接是一种状态实体,通信依赖连接状态的一致性,错误的处理基于连接的上下文。

TCP连接提供的是全双工服务,在一个 TCP 连接中,通信双方(客户端和服务器)可以同时进行数据的发送和接收,发送方和接收方的角色是动态的、非独占的。TCP 连接包含两条独立的逻辑通道,两条通道的数据流互不干扰,各自维护独立的序列号、确认号、滑动窗口等状态信息。

TCP被设计为点对点(Point-to-Point)协议,而非一对多(如组播或广播),这是由其协议特性、设计目标和底层网络架构共同决定的。TCP 的核心定位是在两个端点之间建立一条逻辑连接,通过序列号、确认机制、滑动窗口等复杂机制实现端到端的可靠数据传输。这些机制依赖于唯一的双向通信链路,要求发送方和接收方之间有明确的一一对应关系。若支持一对多(如同时向多个接收方发送数据),每个接收方的网络状态、接收能力、数据确认逻辑均不同,需为每个接收方单独维护一套状态(如序号、窗口、超时定时器等),这会导致协议复杂度呈指数级上升,甚至无法高效管理。

TCP 将数据视为无边界的字节流,保证数据按发送顺序到达接收方。在点对点场景中,通过单一序列号空间即可实现顺序控制;但在一对多场景中,每个接收方的字节流顺序独立,发送方需为每个接收方维护独立的序列号和确认逻辑,这与 TCP 的字节流抽象模型冲突。且一对多属于网络层或应用层的职责,而TCP 专注于传输层的端到端可靠性。

应用进程将数据写入套接字后,会先存于 TCP 发送缓存,等待合适时机发送(数据暂存)。发送缓存配合滑动窗口机制,根据接收方通告的窗口大小,控制发送数据量。若接收方接收缓存快满,通告小窗口值,发送方就减少从发送缓存提取数据发送的量,避免数据丢失(流量控制)。发送缓存中未被确认的数据,在超时未收到确认时会重传(可靠传输保证)。 

TCP 报文段(TCP Segment)是 TCP在网络中传输数据的基本单位,它由首部数据部分 组成,承载着从发送端到接收端的数据以及用于实现可靠传输的控制信息。

TCP接收方将网络中传来的 TCP 报文段,经解析后数据存于接收缓存,供应用进程读取(数据接收缓存)。当收到乱序的报文段,只要序号在接收窗口内,就暂存于接收缓存,等待正确顺序时交付应用进程(乱序处理)。接收缓存剩余空间大小,决定接收方通告给发送方的窗口大小,以此反馈控制发送方发送速率(流量反馈依据)。

TCP可从缓存中取出并且放入报文段的数据数量受限于最大报文段长度(MSS)。MSS 指的是 TCP 报文段中所能承载的应用层数据的最大字节数,不包括 TCP 首部(通常 20 字节 )和 IP 首部(通常 20 字节 )。MSS通常根据最初确定的由本地发送主机发送的最大链路层帧长度,即所谓的最大传输单元(MTU)来设置。

MTU 指的是在数据链路层中,一个数据帧所能够携带的最大有效载荷(不包含帧头、帧尾等链路层控制信息)的字节数。不同的数据链路层协议,MTU 的默认值有所不同 。比如,以太网的 MTU 默认值是 1500 字节,PPP 协议的 MTU 默认值通常为 1500 字节或 296 字节,FDDI(光纤分布式数据接口)的 MTU 则为 4352 字节。

比如,若 MSS 值为 1460 字节,那么加上 20 字节的 TCP 首部和 20 字节的 IP 首部后,整个 IP 数据报长度就是 1500 字节,这恰好是以太网默认的最大传输单元(MTU)。

TCP报文段结构

TCP 报文段首部长度通常为 20 字节(不含TCP可选字段),最多可达 60 字节,包含了实现可靠传输、连接管理等功能的关键控制信息,具体如下:

  1. 源端口号和目的端口号:各占 16 比特,标识发送方和接收方应用进程所使用的端口,帮助数据准确交付给对应的应用程序。
  2. 序号:占 32 比特,标记本报文段在发送方数据字节流中的位置,用于接收方对数据进行排序。
  3. 确认号:占 32 比特,是接收方期望接收的下一个字节的序号,用于向发送方确认已收到的数据。
  4. 首部长度:占 4 比特,指示 TCP 首部的长度,以 4 字节为单位,用于明确首部结束和数据开始的位置。
  5. 标志位:占 6 比特,包含多个标志:
    • URG:紧急标志,URG = 1 时表示报文中有紧急数据。
    • ACK:确认标志,ACK = 1 时确认号字段才有效。
    • PSH:推送标志,指示接收方尽快将数据交付给上层应用。
    • RST:重置标志,用于异常时重置连接。
    • SYN:同步标志,在连接建立时同步序号。
    • FIN:结束标志,在连接释放时通知对方本方无数据发送。
  6. 窗口大小:占 16 比特,接收方通过该字段告知发送方自己当前接收缓冲区的剩余空间大小,实现流量控制。
  7. 校验和:占 16 比特,用于检测 TCP 报文段在传输过程中是否出错,计算范围包括首部、数据部分以及一个伪首部。
  8. 紧急指针:占 16 比特,当 URG 标志位为 1 时有效,指出本报文段中紧急数据的最后一个字节的序号。
  9. 选项:长度可变,最多 40 字节,用于协商额外参数,如最大段大小(MSS)、窗口扩大因子、时间戳等 。
  10. 数据部分:承载的是应用层实际要传输的数据,长度可变,受最大段大小(MSS)限制。在连接建立阶段,客户端和服务器会协商 MSS 值,以确保加上 IP 首部后的整个报文长度不超过链路层的最大传输单元(MTU),避免 IP 分片。

在 TCP 报文段结构中,序号和确认号是实现可靠传输最核心的两个字段。

TCP把数据看成无结构的,有序的字节流。序号是建立在传送的字节流之上,而不是建立在传送的报文段序列之上。因此一个报文段的序号就是该报文段首字节的字节流编号。

序号标识本报文段所携带数据在发送方字节流中的起始位置,用于解决数据分片后的排序问题。例如,若发送方发送的第一个报文段携带 1000 字节数据,序号为500,则下一个报文段的序号为1500。接收方通过序号判断数据是否按序到达,对乱序数据进行缓存或重传请求。在连接建立(三次握手)时,序号用于同步初始序列号(ISN),避免历史数据干扰新连接。

接收方通过确认号告知发送方期望接收的下一个字节的序号,表示 “该序号之前的数据已全部正确接收”。例如,接收方收到序号1-1000的数据后,会返回确认号1001,表示希望接收1001及之后的数据。发送方根据确认号判断哪些数据已被成功接收,哪些需要重传(基于超时重传机制)。同时实现可靠传输的双向确认:不仅发送方需要确认接收方收到数据,接收方也通过确认号主动告知发送方状态。

TCP 通过 “序号 + 确认号” 机制实现数据的有序性、完整性和去重性,是可靠传输的基础,其他字段均围绕二者提供辅助或扩展功能。

往返时间估计与超时

显然,由于路由器的拥塞和端系统负载的变化,TCP报文段中的rtt值也会随之波动,所以任何给定的RTT值也许不是典型的,所以这也造就了一个明显的问题:设置超时间隔的长度。因此为了估计一个典型的RTT,自然要对RTT进行一个均值操作。

TCP 采用一种基于采样的方法来估计 RTT。最初,TCP 会对每个发送的报文段进行计时,当收到对应的 ACK 时,计算实际的往返时间。然后通过加权平均的方式更新对 RTT 的估计值,一旦获得一个新的RTT,TCP就会用以下公式更新均值:

其中α是一个加权因子(通常取值在 0.125 左右 ),SampleRTT是当前测量得到的样本往返时间。

除了估算RTT以外,测量RTT的变化也是很有必要的。DEVrtt用于衡量往返时间估计值的波动情况,是计算超时时间的重要参数之一。

其中,β也是一个加权因子,一般取值为 0.25。该公式的作用是计算往返时间估计值的偏差。|SampleRTT - EstimatedRTT| 表示样本往返时间与估计往返时间的差值的绝对值,通过加权平均的方式更新 DEVrtt ,从而反映出样本往返时间围绕估计往返时间的波动程度。 

既然已经给出了EstimateRTT和DevRTT的值,TCP 的超时间隔(Timeout Interval,简称 RTO)通常使用 :

其中:δ 是一个经验系数, 该系数用于放大 EstimatedRTT 的权重,确保超时间隔足够覆盖网络波动。 4 是 δ 的典型取值,通过将偏差放大 4 倍,使 RTO 对网络延迟的波动更敏感。

可靠数据传输 

在 TCP 协议中,定时器的设置是保障可靠传输的关键机制之一。TCP 使用多种定时器来处理不同场景,其中最核心的是超时重传定时器。

TCP采用的是单计时器模型,发送方每次发送 新数据 时,若当前没有活跃的定时器,则启动一个覆盖 最早未确认数据 的定时器。当收到 最早未确认数据的 ACK 时,停止定时器。

第二个主要事件是超时处理,TCP通过超时重传来响应超时事件,然后重启定时器。 

回退N步or超时重传

TCP 的可靠性机制部分借鉴了 GBN 和 SR 的思想,根据自身需求进行了优化和融合。 

TCP使用滑动窗口机制,但 默认采用累积确认(与 GBN 类似),TCP 的滑动窗口大小可变(动态调整),而 GBN 窗口大小固定。TCP同样为未确认的数据设置定时器,超时后重传 最早未确认的分组及之后的数据(类似 GBN 的回退重传)。但是TCP 引入 快速重传机制,减少不必要的超时等待。

TCP引入 选择性确认(SACK)选项(类似SR),作为累积确认的补充。当接收方收到失序分组时,可通过 SACK 告知发送方具体哪些分段已接收,发送方只需重传未被确认的分段。但SACK 是 TCP 的可选功能(需双方协商启用),而累积确认是默认机制。接收方同样会缓存失序分组(通过接收窗口实现),并在收到丢失分组后按序交付。

超时间隔加倍

在 TCP 协议中,超时间隔加倍(Exponential Backoff) 是一种应对网络拥塞或丢包的重要机制,属于超时重传策略的一部分。当 TCP 发送方多次尝试重传数据仍失败时,会逐步指数级增大超时时间,避免因频繁重传加剧网络拥塞。

当 TCP 发送一个报文段后,会启动一个定时器。若在 RTO 时间内未收到该报文段的确认(ACK),则认为报文段丢失,触发重传

首次超时按正常 RTO 值重传,后续超时每次重传后,将 RTO 值翻倍(即指数级增长)。为避免 RTO 无限增长,TCP 通常设置上限(如 64 秒)。当 RTO 达到上限后,继续重传但不再增加 RTO。

超时间隔加倍避免网络拥塞,若每次超时都以固定时间重传,可能导致大量重传报文段同时涌入网络,加剧拥塞(如 “拥塞崩溃”)。指数退避通过减缓重传频率,给网络留出恢复时间。

区分临时丢包和持续问题:

  • 临时丢包(如网络抖动):较小的 RTO 即可快速恢复。
  • 持续丢包(如链路故障):需要更长的 RTO 避免无效重传。

超时重传 → RTO指数级增长 → 降低重传频率 → 缓解网络拥塞 这一机制是 TCP自适应网络变化的关键,通过动态调整超时时间,在可靠性和效率之间找到平衡,尤其适用于高延迟或不稳定的网络环境。

快速重传

快速重传是 TCP 协议中用于快速恢复丢失报文段的机制,乱序不影响最终确认,但是会触发重传机制。通过 重复确认冗余ACK(Duplicate ACK) 触发重传,无需等待超时定时器到期,从而减少丢包导致的延迟。

接收方通过累加确认,只会确认按序到达的连续字节流末尾,未按序到达的字节会导致后续确认被阻断。

当接收方收到失序报文段时,会发送重复 ACK(即对最近连续收到的最高序号的确认)。发送方收到3 个重复 ACK,判定该序号的报文段丢失,立即重传,无需等待超时。

快速重传 = 3次重复ACK触发 + 立即重传丢失报文段 + 低延迟恢复传输 该机制是 TCP 实现可靠传输高吞吐量的核心技术之一,通过快速响应轻度丢包,显著提升了网络传输效率,尤其适用于实时性要求较高的场景。

流量控制

TCP流量控制为了防止发送方发送速率超过接收方处理能力,避免接收方缓冲区溢出导致丢包。通过动态调整发送窗口大小,使发送速率与接收方处理能力匹配。

核心机制:滑动窗口协议

  1. 接收窗口(Advertised Window, rwnd)

    • 接收方通过 ACK 报文携带rwnd字段,告知发送方自己当前可用的接收缓冲区大小(字节)。
    • 计算公式:rwnd = 接收缓冲区总大小 - 已占用空间
  2. 发送窗口(Send Window)

    • 发送方根据接收方的rwnd动态调整自己的发送窗口大小,确保已发送但未确认的数据量 ≤ rwnd
    • 发送窗口大小 = min (rwndcwnd),其中cwnd为拥塞窗口(由拥塞控制决定)。

初始协商:连接建立时,双方通过 SYN 报文交换初始窗口大小(默认值通常为 4KB)。

动态调整:接收方每处理一批数据,通过 ACK 报文更新rwnd。发送方根据最新rwnd调整发送窗口,控制后续发送速率。

零窗口通知:当接收方缓冲区已满,rwnd = 0,发送方暂停发送,进入 ** 窗口探查(Window Probe)** 状态。

窗口探查:发送方定期发送窗口探测报文(1 字节数据),触发接收方回复最新rwnd,避免死锁。

该机制通过接收窗口实现端到端的速率匹配,确保接收方不会因过载而丢包。UDP没有流量控制,报文段由于缓存溢出可能在接收方丢失。

TCP连接管理 

我们先来详细看一下客户端如何三次握手和服务器建立TCP连接以及四次挥手安全断开连接:

三次握手

1. 客户端 → 服务器:SYN 包
  • 动作
    客户端向服务器发送一个SYN 包(TCP 首部中的 SYN 标志位 = 1),表示请求建立连接。
  • 携带信息
    • 初始序列号(ISN, Initial Sequence Number):客户端随机生成的一个 32 位序列号(如x),用于标识客户端发送的数据字节流起点。
    • 可选参数:如最大段大小(MSS)、窗口缩放因子等。
  • 状态变化
    客户端进入SYN_SENT状态,等待服务器响应。
2. 服务器 → 客户端:SYN+ACK 包
  • 动作
    服务器收到 SYN(同步) 包后,向客户端发送一个SYN+ACK 包(SYN 和 ACK 标志位同时置 1)。
  • 携带信息
    • 服务器的初始序列号(ISN):服务器随机生成的序列号(如y)。
    • 确认号(ACK Number):值为x+1,表示 “已收到客户端的 SYN 包,期望接收客户端下一个序列号为x+1的数据”。
    • 窗口大小(Window Size):服务器的接收窗口大小,告知客户端自己的缓冲区容量。
  • 状态变化
    服务器进入SYN_RCVD状态,等待客户端确认。
3. 客户端 → 服务器:ACK 包
  • 动作
    客户端收到 SYN+ACK 包后,向服务器发送一个ACK 包(ACK 标志位 = 1),SYN=0。
  • 携带信息
    • 确认号:值为y+1,表示 “已收到服务器的 SYN 包,期望接收服务器下一个序列号为y+1的数据”。
    • 序列号:客户端使用x+1作为本次 ACK 的序列号(因 SYN 包本身占用一个序列号)。
  • 状态变化
    客户端和服务器均进入ESTABLISHED状态,连接建立完成,开始数据传输。

四次挥手

1. 主动关闭方 → 被动关闭方:FIN 包
  • 场景
    假设客户端主动发起关闭(也可以是服务器主动关闭)。
  • 动作
    客户端向服务器发送一个FIN 包(TCP 首部中的 FIN 标志位 = 1),表示 “我已无数据要发送,可以关闭连接”。
  • 携带信息
    • 序列号(Seq):客户端当前的序列号u(FIN 包本身占用一个序列号,类似 SYN 包)。
  • 状态变化
    客户端进入FIN_WAIT_1状态,等待服务器的确认。
2. 被动关闭方 → 主动关闭方:ACK 包
  • 动作
    服务器收到 FIN 包后,立即向客户端发送一个ACK 包(ACK 标志位 = 1),表示 “已收到关闭请求,正在处理剩余数据”。
  • 携带信息
    • 确认号(Ack):值为u+1,表示 “已确认收到客户端的 FIN 包”。
    • 序列号(Seq):服务器当前的序列号v(未被 FIN 中断,继续沿用之前的序号)。
  • 状态变化
    服务器进入CLOSE_WAIT状态(半关闭状态),此时服务器仍可向客户端发送剩余数据,客户端仍需接收。
    客户端收到 ACK 后,进入FIN_WAIT_2状态,等待服务器发送 FIN 包。
3. 被动关闭方 → 主动关闭方:FIN 包
  • 动作
    当服务器确认已发送完所有数据后,向客户端发送第二个FIN 包(FIN 标志位 = 1),表示 “我已无数据可发送,正式请求关闭连接”。
  • 携带信息
    • 序列号(Seq):服务器的下一个序列号w(通常w = v + 已发送数据字节数,若中间无新数据,则w = v)。
  • 状态变化
    服务器进入LAST_ACK状态,等待客户端的最终确认。
    客户端收到第二个 FIN 后,进入TIME_WAIT状态。
4. 主动关闭方 → 被动关闭方:ACK 包
  • 动作
    客户端收到服务器的 FIN 包后,向服务器发送最后一个ACK 包,表示 “已确认关闭请求”。
  • 携带信息
    • 确认号(Ack):值为w+1,确认服务器的 FIN 包。
    • 序列号(Seq):客户端的下一个序列号u+1(FIN_WAIT_2 状态时序列号未变化,因无新数据发送)。
  • 状态变化
    • 客户端:发送 ACK 后进入TIME_WAIT状态,并在等待2 倍最大段生存时间(2MSL)后最终进入CLOSED状态。
    • 服务器:收到 ACK 后立即进入CLOSED状态,连接彻底关闭。

客户TCP经历的典型TCP状态序列:

服务器端TCP经历的典型TCP状态序列: 

 为什么必须要三次握手?

  1. 双向确认连接可用性
    • 客户端确认服务器 “能接收数据 + 能发送数据”。
    • 服务器确认客户端 “能接收数据 + 能发送数据”。
  2. 同步初始序列号(Seq)
    避免历史残留报文(延迟到达的旧连接数据包)干扰新连接。
为什么不能用两次握手?

假设改为两次握手(客户端→服务器:SYN;服务器→客户端:SYN+ACK):
存在的风险:

  1. 旧连接残留报文导致的资源浪费
    • 客户端发送的旧 SYN 报文(如因网络延迟滞留)被服务器误认为是新连接请求。
    • 服务器返回 SYN+ACK 并分配资源,但客户端实际未发起新连接,导致服务器端资源(如连接队列、内存)浪费(“半开连接” 问题)。
  2. 单向确认不完整
    • 服务器仅能确认客户端 “能发送数据”,但无法确认客户端 “能接收数据”。
    • 若客户端实际无法接收(如接收缓冲区已满),服务器发送的数据会被丢弃,导致连接不可靠。

结论:两次握手无法避免旧报文干扰,且无法完成双向确认,因此不可行。

四次或更多次握手可行吗?

理论上可行,但存在明显缺陷:

  1. 效率低下
    每增加一次握手,就多一次往返时延(RTT)。例如四次握手需 2 倍 RTT,而三次握手仅需 1.5 倍 RTT,对于高延迟网络(如卫星通信),效率损失显著。
  2. 无额外收益
    三次握手已能完成双向确认和序列号同步,多余的握手次数不会提升可靠性,反而增加协议复杂度。

结论:三次握手是效率与可靠性的最优平衡,多于三次握手无实际意义。

为什么要四次挥手?

TCP 连接是全双工的(双向独立传输),全双工特性要求双向数据流独立关闭:

  1. 允许双向数据流独立关闭
    一方(如客户端)先发送 FIN 包请求关闭 “主动关闭方→被动关闭方” 的数据流,另一方仍可继续发送数据。
  2. 确保数据完整传输
    被动关闭方需先确认收到 FIN 包(避免丢包),待自身数据发送完毕后,再发送 FIN 包关闭反向数据流,确保无数据丢失。
为什么不能用三次挥手?

假设尝试合并两次挥手为一次(如服务器在 ACK 包中同时携带 FIN 标志):
存在的问题:

  • 被动关闭方可能尚未完成数据发送
    服务器收到客户端的 FIN 包后,需先确认(ACK),但此时服务器可能仍有数据未发送完毕。若直接在 ACK 中携带 FIN,会导致服务器被迫中断数据传输,违反 “允许单向数据流持续” 的原则。

示例场景:
客户端发送 FIN 请求关闭 “客户端→服务器” 方向,服务器回复 ACK(确认收到 FIN),并继续发送剩余数据。待数据发送完毕后,服务器再发送 FIN 关闭 “服务器→客户端” 方向。这一过程必须分为两次独立的报文(ACK 和 FIN),因此无法合并为三次挥手。

两次挥手可行吗?

仅在一种特殊场景下可行:被动关闭方在收到 FIN 包时,自身已无数据需要发送。此时可将 ACK 和 FIN 合并为一个报文,形成两次挥手:

  1. 客户端→服务器:FIN(请求关闭)。
  2. 服务器→客户端:FIN+ACK(确认关闭并请求反向关闭)。
  3. 客户端→服务器:ACK(确认反向关闭)。
    但这是特例,而非通用情况,因为 TCP 无法保证被动关闭方在收到 FIN 时恰好无数据待发。

结论:通用场景下必须四次挥手,仅特殊场景可简化为三次,但无法普遍适用两次。

✍🏻✍🏻✍🏻.

相关文章:

  • 基于 Android 和 JBox2D 的简单小游戏
  • Android高级开发第三篇 - JNI异常处理与线程安全编程
  • 用 Whisper 打破沉默:AI 语音技术如何重塑无障碍沟通方式?
  • HTTP、WebSocket、SSE 对比
  • CNN卷积网络:让计算机拥有“火眼金睛“(superior哥AI系列第4期)
  • 打卡day43
  • 秋招Day12 - 计算机网络 - UDP
  • 05.MySQL表的约束
  • 如何区分虚拟货币诈骗与经营失败?
  • STM32G4 电机外设篇(四)DAC输出电流波形 + CAN通讯
  • Vue-3-前端框架Vue基础入门之VSCode开发环境配置和Tomcat部署Vue项目
  • paoxiaomo的XCPC算法竞赛训练经验
  • C++中实现随机数(超详细!​​​​​)
  • 黑马程序员C++核心编程笔记--4 类和对象--多态
  • 1.文件操作相关的库
  • Java Netty 中处理粘包和半包问题的解决方案 | TCP消息完整性校验(XOR )
  • 基于GPT-SoVITS-v4-TTS的音频文本推理,流式生成
  • SOC-ESP32S3部分:25-HTTP请求
  • 移动AI神器GPT Mobile:多模型自由切换
  • 基于SpringBoot运动会管理系统设计和实现(源码+文档+部署讲解)
  • 医疗器械为什么做网站/怎样把个人介绍放到百度
  • 温州网站推广效果/网站优化分析
  • 在线爬取网页源码/合肥网站建设优化
  • 电子书网站 跟我学做家常菜800/百度一下手机版
  • 网站行程表怎么做/济南seo顾问
  • iis网站伪静态/新平台怎么推广