TCP协议

TCP协议是运输层面向连接的、可靠的端到端信息传输协议。在介绍TCP协议之前,我们先看下如何一步步设计出一个可靠数据传输协议。

可靠数据传输协议

rdt1.0:经完全可靠信道传输

这里我们假设数据传输的底层信道是完全可靠的。此时发送端和接收端的有限状态机如下图所示:

  • 虚线:表示来自上层的调用。
  • 带箭头实线:发送/接收端从一个状态变为另一个状态。
  • 横线上方:表示引起状态发生变化的事件。
  • 横线下方:事件发生时所采取的动作。

可以看到,当底层数据传输信道是可靠的时候,即发送端发送的数据保证能够按序到达接收端,此时我们的可靠数据传输协议非常简单,接收端和发送端只需要接收和发送数据即可。

rdt 2.0:经具有比特差错信道传输

假定底层信道可以保证数据有序传输,但是数据在传输过程中某些比特可能出现差错。

在研究一种经过这种信道进行可靠通信的协议之前,首先考虑一下人们会怎样处理这样的情形。考虑一下你自己是怎样通过电话口述一条长报文的。在通常情况下,报文接收者在听到、理解并记下每句话后可能会说"OK’勹如果报文接收者听到一句含糊不清的话时,他可能要求你重复那句容易误解的话。这种口述报文协议使用了肯定确认( positive a•·knowledgment)(" OK" ) 与否定确认(negative acknowledgment) ( h 请重复一遍") 。这些控制报文使得接收方可以让发送方知道哪些内容被正确接收,哪些内容接收有误并因此需要重复。在计算机网络环境中,基于这样重传机制的可靠数据传输协议称为自动重传请求( Automatic Repeat reQuest, ARQ ) 协议。

在ARQ协议中需要三种功能来处理比特差错情况:

  • 差错检测(检验和):接收端收到一个数据包后,要能够判断该数据包在传输过程中是否出现差错。
  • 接收方反馈:接收端发现数据包传出过程中出错了,要通知发送端。
  • 重传:发送端收到出错通知后,要将出错的数据包重新发送。

上图展示了rdt2.0中,发送端和接收的有限状态机。数据传输过程大致如下:

  1. 发送端发送数据后,等待来自接收端的反馈信号。
  2. 接收端收到数据后,检测数据是否出现比特差错,出错,返回一个NAK包,未出错,返回ACK包。
  3. 发送端收到来自接收端的反馈数据,如果是ACK表示数据正确收到,否则表示收到的数据出错。

上述协议存在的一个致命缺陷就是:没有考虑NAK和ACK包受损的情况。一种比较好的方法就是,发送端收到受损ACN或NAK包,直接重传上一个数据分组。不过当发送端收到受损ACK包时,会带来“冗余分组”的问题。

因此,需要在此基础上再引入一种机制:让发送方对其数据分组编号,在数据分组中添加一个新字段,用于存储该分组的编号。这样接收端收到分组后可以根据编号,判断该分组是否已经被正确接收,如果已经被接收了,就丢弃该分组。

rdt 3.0:经具有比特差错的丢包信道传输

在前面的两个版本rdt协议中,我们通过引入检验和、序号、ACK和重传等机制,设计了可以在具有比特差错信道上可靠传输的协议。但是在现实生活中,数据在传出过程中还可能出现丢包现象,该如何处理呢?

一种直接的方法就是:定时重传,发送方发送完数据后,启动一个定时器,如果超过一定时间没有收到接收方的反馈数据,就重传该数据分组。下图展示了在丢包情况下,协议的运作情况:

至此,我们通过:检验和、序号、定时器、肯定和否定确认这些技术,得到了一个可靠数据传输协议。

流水线可靠数据传输协议

rdt3.0是一个正确、可靠的协议,但是它的性能并非让人满意。它是一个停等协议,即发送一个数据分组后,发送端要等待收到反馈数据分组,才继续发送下一个数据分组。

对于这种性能问题,一种简单的解决方法就是:不以停等方式运行,允许发送方发送多个分组而无需等待确认。

如上图所示,如果发送方可以在等待确认之前发送3个报文,其利用率基本上提高了3倍。但是在流水线技术下,我们需要对传输协议作如下改进:

  1. 增大序号范围,rdt3.0只需要0和1两个序号即可,在流水线技术中需要更大的序号范围
  2. 缓存分组,发送方最低限度应当能缓冲那些已发送但没有确认的分组。如下面讨论的那样,接收方或许也需要缓存那
    些巳正确接收的分组。

所需序号范围和对缓冲的要求取决于数据传输协议如何处理丢失、损坏及延时过大的分组。解决流水线的差错恢复有两种基本方法是:回退N 步(Go-Back- N,GBN) 和选择重传(Selective Repeat, SR ) 。

回退N步

在回退N 步(GBN ) 协议中,允许发送方发送多个分组(当有多个分组可用时)而不需等待确认,但它也受限于在流水线中未确认的分组数不能超过某个最大允许数N

上图展示了GBN协议中,发送方的窗口信息。我们为什么先要限制这些被发送的、未被确认的分组的数目为N 呢? 为什么不允许这些分组为无限制的数目呢?其中流量控制是对发送方施加限制的原因之一。

协议的名字“回退N 步”来源于出现丢失和时延过长分组时发送方的行为。就像在停等协议中那样,定时器将再次用千恢复数据或确认分组的丢失。如果出现超时,发送方重传所有已发送但还未被确认过的分组。

在GBN 中,接收方的动作也很简单。如果一个序号为n 的分组被正确接收到,并且按序(即上次交付给上层的数据是序号为n -1 的分组),则接收方为分组n 发送一个ACK, 并将该分组中的数据部分交付到上层。在所有其他情况下,接收方丢弃该分组,并
为最近按序接收的分组重新发送ACK。下图展示了GBN协议的运行流程:

在GBN 协议中,接收方丢弃所有失序分组,尽管这可能是一个正确接收(但失序)的分组,这也是GBN协议的主要缺点。

选择重传

前面也提到过,单个分组的差错就能够引起GBN 重传大量分组,许多分组根本没有必要重传,这大大降低了数据传输效率。

选择重传(SR) 协议通过让发送方仅重传那些它怀疑在接收方出错(即丢失或受损)的分组而避免了不必要的重传。这时需要接收方也要维护一个窗口,标记那些数据分组是失序的,但是没有出错。

SR 接收方将确认一个正确接收的分组而不管其是否按序。失序的分组将被缓存直到所有丢失分组(即序号更小的分组)皆被收到为止,这时才可以将一批分组按序交付给上层。下图展示了SR协议运作流程:

这里需要注意的一个问题就是:分组需要范围和窗口大小。下图展示了一个窗口大小和分组范围都为3的SR协议:

当接收方窗口太大或者序号范围太小时,可能导致接收方无法判断数据是一个新分组还是一次重传。因此窗口长度必须小于或等于序号空间大小的一半

TCP协议

TCP概述

TCP协议是一个面向连接、点对点、全双工的运输层可靠数据传输协议,实现了重传机制、滑动窗口、流量控制和拥塞控制。TCP头部格式如下:

  • 端口号:包括源端口号和目的端口号,用于多路复用和多路分解,将TCP连接和上层应用对应上。
  • 序列号:建立连接时随机生成,用于数据分组的编号,每发送一个,数值加1。 用来解决网络包乱序问题。
  • 确认应答号: 指下一次「期望」收到的数据的序列号,发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。用来解决丢包的问题。
  • 控制位
    • ACK:该位为 1 时,「确认应答」的字段变为有效,TCP 规定除了最初建立连接时的 SYN 包之外该位必须设置为 1
    • RST:该位为 1 时,表示 TCP 连接中出现异常必须强制断开连接。
    • SYN:该位为 1 时,表示希望建立连接,并在其「序列号」的字段进行序列号初始值的设定。
    • FIN:该位为 1 时,表示今后不会再有数据发送,希望断开连接。当通信结束希望断开连接时,通信双方的主机之间就可以相互交换 FIN 位为 1 的 TCP 段。

TCP建立连接

TCP 是面向连接的协议,所以使用 TCP 前必须先建立连接,而建立连接是通过三次握手来进行的。三次握手的过程如下图:

  1. 一开始,客户端和服务端都处于 CLOSED 状态。先是服务端主动监听某个端口,处于 LISTEN 状态。
  2. 第一次:客户端会随机初始化序号(client_isn),将此序号置于 TCP 首部的「序号」字段中,同时把 SYN 标志位置为 1 ,表示 SYN 报文。接着把第一个 SYN 报文发送给服务端,表示向服务端发起连接,该报文不包含应用层数据,之后客户端处于 SYN-SENT 状态。
  3. 第二次:服务端收到客户端的 SYN 报文后,首先服务端也随机初始化自己的序号(server_isn),将此序号填入 TCP 首部的「序号」字段中,其次把 TCP 首部的「确认应答号」字段填入 client_isn + 1, 接着把 SYNACK 标志位置为 1。最后把该报文发给客户端,该报文也不包含应用层数据,之后服务端处于 SYN-RCVD 状态。
  4. 第三次:客户端收到服务端报文后,还要向服务端回应最后一个应答报文,首先该应答报文 TCP 首部 ACK 标志位置为 1 ,其次「确认应答号」字段填入 server_isn + 1 ,最后把报文发送给服务端,这次报文可以携带客户到服务器的数据,之后客户端处于 ESTABLISHED 状态。
  5. 服务器收到客户端的应答报文后,也进入 ESTABLISHED 状态。

TCP连接销毁

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 状态,至此客户端也完成连接的关闭。

你可以看到,每个方向都需要一个 FIN 和一个 ACK,因此通常被称为四次挥手

这里一点需要注意是:主动关闭连接的,才有 TIME_WAIT 状态。

TCP流量控制

为什么要流量控制?

TCP连接中,接收双方都会维护一个接收缓存。当该TCP连接收到正确、按序的字节后,它就将数据放人接收缓存。相关联的应用进程会从该缓存中读取数据,但不必是数据刚一到达就立即读取。事实上,接收方应用也许正忙千其他任务,甚至要过很长时间后才去读取该数据。如果某应用程序读取数据时相对缓慢,而发送方发送得太多、太快,发送的数据就会很容易地使该连接的接收缓存溢出

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

TCP 通过让发送方维护一个称为接收窗口(receive window) 的变量来提供流量控制。通俗地说,接收窗口用于给发送方一个指示——该接收方还有多少可用的缓存空间。

TCP拥塞控制

为什么要有拥塞控制?和前面流量控制有什么不同?

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

一般来说,计算机网络都处在一个共享的环境。因此也有可能会因为其他主机之间的通信使得网络拥堵。

在网络拥堵的时候,丢包现象频繁,但是由于TCP协议的重传机制,可能会陷入丢包,重传,重传又丢包,丢包又重传的恶性循环,加剧网络拥堵。

下面详细介绍下TCP拥塞控制算法。该算法主要包括3个主要部分:慢启动,拥塞避免和快速回复。

  • 慢启动状态

    • 当一条TCP连接开始时,cwnd 的值通常初始置为一个MSS(最大报文段长度) 的较小值。
    • 每当传输的报文段首次被确认,cwnd就增加1倍 。

    因此,TCP发送速率起始慢,但在慢启动阶段以指数增长。但是,**何时结束这种指数增长呢?**慢启动对这个问题提供了几种答案。

    • 如果存在一个由超时指示的丢包事件( 即拥塞), TCP 发送方将cwnd 设置为1 并重新开始慢启动过程。并且将慢启动阈值ssthresh设置为cwnd/2。
    • 慢启动结束的第二种方式是直接与ssthresh 的值相关联。因为当检测到拥塞时ssthresh 设为cwnd 的值一半,当到达或超过ssthresh 的值时,继续使cwnd 翻番可能有些鲁莽。因此,当cwnd的值等于sstbresh 时,结束慢启动并且TCP 转移到拥塞避免模式
  • 拥塞避免状态

    一旦进入拥塞避免状态, cwnd 的值大约是上次遇到拥塞时的值的一半,即距离拥塞可能并不遥远!因此, TCP 无法每过一个Rtt再将cwnd 的值翻番,而是采用了一种较为保守的方法,每个Rtt只将cwnd 的值增加一个MSS。

    但是何时应当结束拥塞避免的线性增长(每RTT 1MSS ) 呢?当出现超时时, 与慢启动的情况一样, cwnd 的值被设置为1个MSS, 当丢包事件出现时, ssthresh 的值被更新为cwnd 值的一半。

    然而,前面讲过丢包事件也能由一个三个冗余ACK 事件触发。在这种情况下,网络继续从发送方向接收方交付报文段(就像由收到冗余ACK 所指示的那样) 。因此TCP 对这种丢包事件的行为,相比于超时指示的丢包,应当不那么剧烈: TCP 将cwnd 的值减半(为使测量结果更好,计及已收到的3 个冗余的ACK 要加上3 个MSS), 并且当收到3 个冗余的ACK, 将ssthresh 的值记录为cwnd 的值的一半。接下来进入快速恢复状态。

  • 快速修复状态

    进入快速恢复算法如下:

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

常见问题

TCP vs UDP

  • TCP面向连接,有重传、拥塞控制、流量控制等机制,UDP无连接;
  • TCP传输可靠,UDP不可靠;
  • TCP传字节流,UDP传数据报文段;
  • TCP传输慢,需要资源多,UDP传输快,需要资源少;
  • TCP头部20-60字节,UDP8字节;
  • UDP主要用于即时通信,比如 QQ 语音、 QQ 视频 、直播等等。

TCP 三次握手

为什么是三次握手?不是两次、四次?

我们知道 TCP 连接:用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括Socket、序列号和窗口大小称为连接。

总之:TCP 建立连接时,通过三次握手能防止历史连接的建立,能减少双方不必要的资源开销,能帮助双方同步初始化序列号。序列号能够保证数据包不重复、不丢弃和按序传输。

不使用「两次握手」和「四次握手」的原因:

  • 「两次握手」:无法防止历史连接的建立(比如之前该client发送的一个SYN报文在网络中滞留很久,然后被服务器收到了,服务器误以为是客户端新发过来的连接请求),会造成双方资源的浪费,也无法可靠的同步双方序列号(这个是主要原因);
  • 「四次握手」:三次握手就已经理论上最少可靠连接建立,所以不需要使用更多的通信次数。

其他问题

  1. 为什么需要 TCP 协议? TCP 工作在哪一层?

    IP 层是「不可靠」的,它不保证网络包的交付、不保证网络包的按序交付、也不保证网络包中的数据的完整性。

  2. 什么是 TCP ?

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

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

    简单来说就是,用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括Socket、序列号和窗口大小称为连接。

  2. 如何唯一确定一个 TCP 连接呢?

源地址,源端口,目的地址,目的端口

  1. 有一个 IP 的服务器监听了一个端口,它的 TCP 的最大连接数是多少?

最大连接数 = IP数 × 端口数

打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2021-2022 Yin Peng
  • 引擎: Hexo   |  主题:修改自 Ayer
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信