详解 TCP/IP

TCP/IP 协议模型

TCP/IP 协议模型(Transmission Control Protocol/Internet Protocol),包含了一系列构成互联网基础的网络协议,是 Internet 的核心协议。

基于 TCP/IP 的参考模型将协议分成四个层次,它们分别是网络接口层网络层传输层应用层

但实质上,TCP/IP 只有最上面的三层,因为最下面的网络接口层基本上和一般的通信链路的功能上没有多大差别,对于计算机网络来说,这一层并没有什么特别新的具体的内容,因此在学习计算机网络原理是往往采用折中的办法,即综合 OSI 和 TCP/IP 的优点,采用一种只有五层协议的体系结构,这样既简洁又能将概念阐述清楚。

数据进入协议栈时的封装过程如下图所示:

数据发送方每层不断地封装首部与尾部,添加一些传输的信息,确保能传输到目的地。出栈的过程,数据接收方每层不断地拆除首部与尾部,得到最终传输的数据。

物理层与数据链路层

物理层负责 0、1 比特流与物理设备电压高低、光的闪灭之间的互换。数据链路层负责将 0、1 序列划分为数据帧从一个节点传输到临近的另一个节点,这些节点是通过 MAC 来唯一标识的(MAC,物理地址,一个主机会有一个 MAC 地址)。

  1. 封装成帧: 把网络层数据报加头和尾,封装成帧,帧头中包括源 MAC 地址和目的 MAC 地址。
  2. 透明传输:零比特填充、转义字符。
  3. 可靠传输: 在出错率很低的链路上很少用,但是无线链路 WLAN 会保证可靠传输。
  4. 差错检测(CRC):接收者检测错误,如果发现差错,丢弃该帧。

网络层

网络层提供路由和寻址的功能,使两终端系统能够互连且决定最佳路径,并具有一定的拥塞控制和流量控制的能力。相当于发送邮件时需要地址一般重要。由于 TCP/IP 协议体系中的网络层功能由 IP 协议规定和实现,故又称 IP 层。

常见协议有 IP(v4,v6)、ICMP、IGMP、OSPF 等。

IP 协议

IP 协议是 TCP/IP 协议的核心,所有的 TCP,UDP,IMCP,IGMP 的数据都以 IP 数据格式传输。

IP 不是可靠的,无连接的协议,它不保证数据能准确的传输。数据包在到达的时候可能已经损坏,顺序错乱。保证可靠性的工作交给 IP 上层协议 TCP 或 UDP 来做。

在数据链路层中我们一般通过 MAC 地址来识别不同的节点,而在 IP 层我们也要有一个类似的地址标识,这就是 IP 地址。

IPv4 和 IPv6

IPv4 是 IP 协议的第四个版本,使用 32 位(4 字节)地址,因此地址空间中只有 $2^{32}$ 个地址,随着地址不断被分配给最终用户,IPv4 地址枯竭问题也在随之产生。因此诞生了 IPv6。

IPv6 定义了一种新的分组格式,目的是为了最小化路由器处理的消息标头。由于 IPv4 消息和 IPv6 消息标头有很大不同,因此这两种协议无法互操作。但是在大多数情况下,IPv6 仅仅是对 IPv4 的一种保守扩展。除了嵌入了互联网地址的那些应用协议以外,大多数传输层和应用层协议几乎不怎么需要修改就可以在 IPv6 上运行。

IPv6 具有比 IPv4 大得多的编码地址空间。这是因为 IPv6 采用 128 位的地址,而 IPv4 使用的是 32 位。因此新增的地址空间支持 $2^{128}$

IPv6 具有更快的传输速度,因为 IPv6 是固定报头,不像 IPv4 那样携带一堆冗长的数据。

ICMP 协议

ICMP(Internet Control Message Protocol) 它用于 IP 协议中发送控制消息。也就是说,ICMP 是依靠 IP 协议来完成信息发送的。

ICMP 协议和 TCP、UDP 等协议不同,它不用于传输数据,只是用来发送消息。因为 IP 协议现在有两类版本:IPv4 和 IPv6 ,所以 ICMP 也有两个版本:ICMPv4 和 ICMPv6。

ICMP 的主要功能有两个:

  1. 确认 IP 包是否能够成功到达目标地址,当两个设备通过互联网相连时,任意一个设备发送给另一个设备的 IP 包如果没有到达,就会生成 ICMP 数据包发送给设备共享。
  2. 进行网络诊断,比如我们常用 ping 命令确认两个互联网设备是否连接正常,ping 通常用来测试两台主机之间的连接速度,并准确报告数据包到达目的地并返回后所花费的时间。
ping 的发送和接收过程

同个子网下的主机 A 和 主机 B,主机 A 执行 ping 主机 B 后,过程如下:

ping 命令执行的时候,源主机首先会构建一个 ICMP 回送请求消息数据包。

ICMP 数据包内包含多个字段,最重要的是两个:

  1. 第一个是类型,对于回送请求消息而言该字段为 8;
  2. 另外一个是序号,主要用于区分连续 ping 的时候发出的多个数据包。

每发出一个请求数据包,序号会自动加 1。为了能够计算往返时间 RTT,它会在报文的数据部分插入发送时间。

然后,由 ICMP 协议将这个数据包连同 B 的地址一起交给 IP 层。IP 层将以 B 的地址作为目的地址,本机 IP 地址作为源地址,协议字段设置为 1 表示是 ICMP 协议,在加上一些其他控制信息,构建一个 IP 数据包。

接下来,需要加入 MAC 头。如果在本地 ARP 映射表中查找出 B 的地址 所对应的 MAC 地址,则可以直接使用;如果没有,则需要发送 ARP 协议查询 MAC 地址,获得 MAC 地址后,由数据链路层构建一个数据帧,目的地址是 IP 层传过来的 MAC 地址,源地址则是本机的 MAC 地址;还要附加上一些控制信息,依据以太网的介质访问规则,将它们传送出去。

主机 B 收到这个数据帧后,先检查它的目的 MAC 地址,并和本机的 MAC 地址对比,如符合,则接收,否则就丢弃。

接收后检查该数据帧,将 IP 数据包从帧中提取出来,交给本机的 IP 层。同样,IP 层检查后,将有用的信息提取后交给 ICMP 协议。

主机 B 会构建一个 ICMP 回送响应消息数据包,回送响应数据包的类型字段为 0,序号为接收到的请求数据包中的序号,然后再发送出去给主机 A。

在规定的时候间内,源主机如果没有接到 ICMP 的应答包,则说明目标主机不可达;如果接收到了 ICMP 回送响应消息,则说明目标主机可达。

此时,源主机会检查,用当前时刻减去该数据包最初从源主机上发出的时刻,就是 ICMP 数据包的时间延迟。

ARP 及 RARP 协议

ARP 是根据 IP 地址获取 MAC 地址的一种协议。

ARP(地址解析)协议是一种解析协议,本来主机是完全不知道这个 IP 对应的是哪个主机的哪个接口,当主机要发送一个 IP 包的时候,会首先查一下自己的 ARP 高速缓存(就是一个 IP-MAC 地址对应表缓存)。

如果查询的 IP-MAC 值对不存在,那么主机就向网络发送一个 ARP 协议广播包,这个广播包里面就有待查询的 IP 地址,而直接收到这份广播的包的所有主机都会查询自己的 IP 地址,如果收到广播包的某一个主机发现自己符合条件,那么就准备好一个包含自己的 MAC 地址的 ARP 包传送给发送 ARP 广播的主机。

而广播主机拿到 ARP 包后会更新自己的 ARP 缓存(就是存放 IP-MAC 对应表的地方)。发送广播的主机就会用新的 ARP 缓存数据准备好数据链路层的的数据包发送工作。

而 RARP 协议的工作与此相反。

传输层

传输层服务通过传输层协议的编程接口传递给应用进程。该服务可以包括以下功能:

  • 连接导向式通信(面向连接):通常对于一个应用进程来说,把连接解读为数据流而非处理底层的无连接模型(如用户数据报协议(UDP)与网际协议(IP)的资料包模型)更加容易。
  • 相同次序交付:网络层通常不保证数据包到达顺序与发送顺序相同,但这往往是一个可取的特点。这通常是通过给报文段编号来完成的,接收者按次序将它们传给应用进程。这可能会造成队头阻塞
  • 可靠性:由于网络拥塞和错误,数据包可能在传输过程中丢失。通过检错码(如校验和),传输协议可以检查数据是否损坏,并通过向发送者传 ACK 或 NACK 消息确认正确接收。自动重发请求方案可用于重新传输丢失或损坏的数据。
  • 流量控制:有时必须控制两个节点之间的数据传输速率以阻止快速的发送者传输超出接收缓冲器所能承受的数据,造成缓冲区溢出。这也可以通过减少缓冲区不足来提高效率。
  • 拥塞避免:拥塞控制可以控制进入到电信网络的流量。
  • 多路复用:端口可以在单个节点上提供多个端点。例如,邮政地址的名称是一种多路复用,并区分同一位置的不同收件人。每个计算机应用进程会监听它们自己的端口,这使得在同一时间可以使用多个网络服务。它是在 TCP/IP 模型中是传输层的一部分,但在 OSI 模型中属于会话层。

三次握手与四次挥手

之前写过这方面的文章,请看三次握手与四次挥手

流量控制

如果发送方把数据发送得过快,接收方要处理多方信息,可能会来不及接收,这就会造成数据的丢失。所谓流量控制就是让发送方的发送速率不要太快,要让接收方来得及接收。

利用滑动窗口机制可以很方便地在 TCP 连接上实现对发送方的流量控制。

设 A 向 B 发送数据。在连接建立时,B 告诉了 A:“我的接收窗口是 rwnd = 400 ”(这里的 rwnd 表示 receiver window) 。因此,发送方的发送窗口不能超过接收方给出的接收窗口的数值。请注意,TCP 的窗口单位是字节,不是报文段。假设每一个报文段为 100 字节长,而数据报文段序号的初始值设为 1。大写 ACK 表示首部中的确认位 ACK,小写 ack 表示确认字段的值 ack。

从图中可以看出,B 进行了三次流量控制。第一次把窗口减少到 rwnd = 300 ,第二次又减到了 rwnd = 100 ,最后减到 rwnd = 0 ,即不允许发送方再发送数据了。这种使发送方暂停发送的状态将持续到主机 B 重新发出一个新的窗口值为止。B 向 A 发送的三个报文段都设置了 ACK = 1 ,只有在 ACK=1 时确认号字段才有意义。

TCP 为每一个连接设有一个持续计时器(persistence timer)。只要 TCP 连接的一方收到对方的零窗口通知,就启动持续计时器。若持续计时器设置的时间到期,就发送一个零窗口控测报文段(携 1 字节的数据),那么收到这个报文段的一方就重新设置持续计时器。

拥塞控制

慢开始和拥塞避免

发送方维持一个拥塞窗口 cwnd ( congestion window )的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化。发送方让自己的发送窗口等于拥塞窗口。

发送方控制拥塞窗口的原则是:只要网络没有出现拥塞,拥塞窗口就再增大一些,以便把更多的分组发送出去。但只要网络出现拥塞,拥塞窗口就减小一些,以减少注入到网络中的分组数。

慢开始算法

当主机开始发送数据时,如果立即所大量数据字节注入到网络,那么就有可能引起网络拥塞,因为现在并不清楚网络的负荷情况。因此,较好的方法是 先探测一下,即由小到大逐渐增大发送窗口,也就是说,由小到大逐渐增大拥塞窗口数值。

通常在刚刚开始发送报文段时,先把拥塞窗口 cwnd 设置为一个最大报文段 MSS 的数值。而在每收到一个对新的报文段的确认后,把拥塞窗口增加至多一个 MSS 的数值。用这样的方法逐步增大发送方的拥塞窗口 cwnd ,可以使分组注入到网络的速率更加合理。

每经过一个传输轮次,拥塞窗口 cwnd 就加倍。一个传输轮次所经历的时间其实就是往返时间 RTT。不过“传输轮次”更加强调:把拥塞窗口 cwnd 所允许发送的报文段都连续发送出去,并收到了对已发送的最后一个字节的确认。

另外,慢开始的“慢”并不是指 cwnd 的增长速率慢,而是指在 TCP 开始发送报文段时先设置 cwnd=1,使得发送方在开始时只发送一个报文段(目的是试探一下网络的拥塞情况),然后再逐渐增大 cwnd。

为了防止拥塞窗口 cwnd 增长过大引起网络拥塞,还需要设置一个慢开始门限 ssthresh 状态变量。慢开始门限 ssthresh 的用法如下:

  • cwnd < ssthresh 时,使用上述的慢开始算法。
  • cwnd > ssthresh 时,停止使用慢开始算法而改用拥塞避免算法。
  • cwnd = ssthresh 时,既可使用慢开始算法,也可使用拥塞控制避免算法。

让拥塞窗口 cwnd 缓慢地增大,即每经过一个往返时间 RTT 就把发送方的拥塞窗口 cwnd 加 1,而不是加倍。这样拥塞窗口 cwnd 按线性规律缓慢增长,比慢开始算法的拥塞窗口增长速率缓慢得多。这就是拥塞避免

无论在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(其根据就是没有收到确认),就要把慢开始门限 ssthresh 设置为出现拥塞时的发送 方窗口值的一半(但不能小于 2)。然后把拥塞窗口 cwnd 重新设置为 1,执行慢开始算法。

快重传和快恢复

快重传算法首先要求接收方每收到一个失序的报文段后就立即发出重复确认(为的是使发送方及早知道有报文段没有到达对方)而不要等到自己发送数据时才进行捎带确认。

接收方收到了 M1 和 M2 后都分别发出了确认。现在假定接收方没有收到 M3 但接着收到了 M4。

显然,接收方不能确认 M4,因为 M4 是收到的失序报文段。根据可靠传输原理,接收方可以什么都不做,也可以在适当时机发送一次对 M2 的确认。

但按照快重传算法的规定,接收方应及时发送对 M2 的重复确认,这样做可以让 发送方及早知道报文段 M3 没有到达接收方。发送方接着发送了 M5 和 M6。接收方收到这两个报文后,也还要再次发出对 M2 的重复确认。这样,发送方共收到了 接收方的四个对 M2 的确认,其中后三个都是重复确认。

快重传算法还规定,发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段 M3,而不必 继续等待 M3 设置的重传计时器到期。

与快重传配合使用的还有快恢复算法,其过程有以下两个要点:

  • 当发送方连续收到三个重复确认,就执行“乘法减小”算法,把慢开始门限 ssthresh 减半。
  • 与慢开始不同之处是现在不执行慢开始算法(即拥塞窗口 cwnd 现在不设置为 1),而是把 cwnd 值设置为 慢开始门限 ssthresh 减半后的数值,然后开始执行拥塞避免算法(“加法增大”),使拥塞窗口缓慢地线性增大。

TCP TAHOE 和 TCP RENO

TCP TAHOE 和 TCP RENO 是拥塞控制的两种形式,特别是在接收 3 个重复的 ack 时。

Tahoe:处理 3 个重复的类似(确切地说?)接收超时。它首先执行快速重传。然后,它将 ssthresh 值减半到原始拥塞窗口大小,并将新窗口大小设置为 1 并保持慢启动。

Reno:Tahoe 的继任者,在收到三个重复的 ack 后进入快速恢复模式,从而将 ssthresh 值减半。对于每个连续的重复 ack(第四,第五,第六),cwind 增加 1.一旦接收器最终收到丢失的数据包,TCP 将在超时时转移到拥塞避免或缓慢状态。

如何解决 TCP 丢包的问题

为了满足 TCP 协议不丢包。TCP 协议有如下规定:

  1. 数据分片:发送端对数据进行分片,接受端要对数据进行重组,由 TCP 确定分片的大小并控制分片和重组。
  2. 到达确认:接收端接收到分片数据时,根据分片数据序号向发送端发送一个确认。
  3. 超时重发:发送方在发送分片时设置超时定时器,如果在定时器超时之后没有收到相应的确认,重发分片数据。
  4. 滑动窗口:TCP 连接的每一方的接受缓冲空间大小固定,接收端只允许另一端发送接收端缓冲区所能接纳的数据,TCP 在滑动窗口的基础上提供流量控制,防止较快主机致使较慢主机的缓冲区溢出。
  5. 失序处理:作为 IP 数据报来传输的 TCP 分片到达时可能会失序,TCP 将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层。
  6. 重复处理:作为 IP 数据报来传输的 TCP 分片会发生重复,TCP 的接收端必须丢弃重复的数据。
  7. 数据校验:TCP 将保持它首部和数据的检验和,这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到分片的检验或有差错,TCP 将丢弃这个分片,并不确认收到此报文段导致对端超时并重发。

参考文章