计算机网络(四):传输层

传输层位于应用层和网络层之间,是分层的网络体系结构的重要组成部分,该层为运行在不同主机上的应用程序进程提供直接的通信服务起着至关重要的作用。

1. 概述和传输层服务

传输层协议为运行在不同端系统上的应用进程之间提供逻辑通信(logic communication)功能,应用层进程使用传输层提供的逻辑通信功能而无需考虑实现通信的物理基础设施的细节。

传输层协议是在端系统中而不是在路由器中实现的。

在发送端,运输层将从发送应用程序进程接收到的报文转换成传输层分组(传输层报文段(segment))。实现的方法可能是将报文划分成较小的块,然后为每块加上传输层首部来生成传输层报文段。然后传输层将这些报文段传递给网络层,网络层再将其封装为网络层分组(数据报),并向目的地发送。

在接收端,网络层从数据报中提取传输层报段,并将报文段上交给传输层,传输层处理接收到的报文段,使该报文段中的数据为接收应用进程使用。

在TCP/IP中能够实现传输层功能的、具有代表性的协议是 TCP 和 UDP

在简单介绍UDP和TCP之前,应该简单介绍一下网络层,网络层协议有一个名字即IP,即网际协议。IP为主机间提供逻辑通信,IP的服务模型为尽力而为交付服务(best-effort delivery service)这意味着IP尽最大的努力在主机间交付报文段,但是不做任何保证。即IP提供一种不可靠服务。另外,每台主机都需要有一个网络层地址,即IP地址。

  • TCP:传输控制协议
    • TCP是面向连接的、可靠的流协议。
    • TCP为提供可靠性传输,实行“顺序控制”或“重发控制”机制。此外还具备“流控制(流量控制)”、“拥塞控制”、提高网络利用率等众多功能。
  • UDP:用户数据报协议
    • UDP是不具有可靠性的数据报协议。细微的处理它会交给上层的应用去完成。

在这里插入图片描述

UDP和TCP最基本的责任是,将两个端系统间IP的交付服务扩展为运行在端系统上的两个进程之间的交付服务。称为传输层的多路复用( transport -layer multiplexing) 与多路分解( demultiplexing)。

  • 多路复用
    • 在发送端源主机中不同套接字中收集数据块,并为每个数据块封装上首部信息,从而生成报文段,然后将报文段传递到网络层,多个套接字复用一个传输。
  • 多路分解
    • 在接收端,传输层检查发送来的报文段,通过发送端的首部信息,标识套接字,进而将报文段定向到正确的套接字中。

这些用来标识的字段称为源端口号字段 (source port oumber field) 和目的端口号字段 (destination port nurnber field) 。

在这里插入图片描述

2. UDP:用户数据报协议

UDP 是一个简单的面向数据报的运输层协议:进程的每个输出操作都正好产生一个 UDP 数据报,并组装成一份待发送的 IP 数据报。

在这里插入图片描述

UDP不提供可靠性:它把应用程序传给 IP层的数据发送出去,但是并不保证它们能到达目的地。由于缺乏可靠性,我们似乎觉得要避免使用 UDP而使用一种可靠协议如 TCP。但是并不是这样,许多应用更适合用UDP,原因包括如下几点:

  • 关于发送什么数据以及何时发送的应用层控制更为精细
    • 采用 UDP 时,只要应用进程将数据传递给 UDP,UDP 就会将此数据打包进 UDP 报文段并立即将其传递给网络层。
    • 而 TCP 可能会进行拥塞控制等等操作,这在之后继续讨论。
    • 因为实时应用通常要求最小的发送速率,不希望过分地延迟报文段的发送,且能够忍受一些数据丢失,所以更适合使用UDP。
  • 无须建立连接
    • TCP 在开始数据传输之前要经过 次握手,UDP 却不需要任何准备即可进行数据传输 因此 UDP 不会引人建立连接的时延。
  • 无连接状态
    • TCP 需要在端系统中维护连接状态,此连接状态包括接收和发送缓存、拥塞控制参数以及序号与确认号的参数。当然会消耗一定得资源。
    • UDP 不维护连接状态,也不跟踪这些参数,这样可能能支持更多的活跃用户。
  • 分组首部开销小:
    • 每个 TCP 报文段都有 20 字节的首部开销,而 UDP 仅有 8 字节的开销

2.1 UDP 报文段结构

在这里插入图片描述

UDP 首部包含了四个字段,每个字段由两个字节组成。

  • 端口号表示发送进程和接收进程。用于将应用数据分发给相应的进程。由于 IP 层已经把 IP数据报分配给TCP或UDP,所以UDP端口和TCP端口是相互独立的。
  • 长度字段指示了UDP报文段中的字节数(首部 + 数据)。
  • 校验和来检查该报文段是否出现了差错。

UDP 检验和

UDP 检验和提供了差错检测功能 这就是说,检验和用于确定当 UDP 报文段从源到达目的地移动时,其中的比特是否发生了改变。

发送方的 UDP 对报文段中的所有 16 比特字的和进行反码运算,计算出检验和。接收方接受到之后将所有16 比特字和检验和进行相加,如果和是1111111111111111,则没有差错,反之则出现差错。

如果发送端没有计算检验和而接收端检测到检验和有差错,那么 UDP数据报就要被悄悄地丢弃。不产生任何差错报文。

UDP检验和是一个端到端的检验和。它由发送端计算,然后由接收端验证。其目的是为了发现UDP首部和数据在发送端到接收端之间发生的任何改动。

3. TCP:传输控制协议

3.1 TCP 服务

尽管 TCP 和 UDP 都使用相同的网络层(IP),TCP却向应用层提供与 UDP 完全不同的服务。TCP提供一种面向连接的、可靠的字节流服务。

另外,TCP连接提供的是全双工服务,即进程A向进程B存在一条TCP连接,那么应用层数据就可以在从进程B流向进程A的同时,也从进程A流向进程B。TCP连接也总是点对点的。仅有两方进行彼此通信。

TCP 主要通过下列方式来提供可靠性:

  • 应用数据被分割成 TCP 认为最适合发送的数据块。这和 UDP 完全不同,应用程序产生的数据报长度将保持不变。由 TCP传递给I P的信息单位称为报文段或段( segment)。
  • 当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。
  • 当TCP收到发自TCP连接另一端的数据,它将发送一个确认。这个确认不是立即发送,通常将推迟几分之一秒。
  • TCP将保持它首部和数据的检验和。目的是检测数据在传输过程中的任何变化。
  • 如果必要,TCP将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层。对于重复的数据会进行丢弃
  • TCP还能提供流量控制。TCP连接的每一方都有固定大小的缓冲空间。

3.2 TCP 报文段结构

TCP 报文段由首部字段和一个数据字段组成,数据字段包含一块应用数据,最大报文段长度 (Maximum Segmenl Size , MSS) 限制了报文段数据字段的最大长度,MSS 通常根据最初确定的由
本地发送主机发送的最大链路层帧长度(即所谓的最大传输单元 (Maximum Transmission Unit , MTU)) 来设置。所以当TCP发送一个大文件的时候,TCP通常是将该文件划分成长度为MSS的若干块。

在这里插入图片描述

  • 和UDP一样,首部包括源端口号和目的端口号,用于将数据复用/分解到不同进程。TCP首部也同样包括检验数据是否发生改变的校验和字段。

  • 32位的序号字段 (sequence number field) 32 位的确认号字段( acknowl-edgment number field),用于实现可靠数据传输之后进行具体阐述。

  • 16位的接受窗口字段,用于流程控制。该字段用于指示接受方愿意接受的字节数量。

  • 4位的首部长度字段,该字段指示了以32位的字为单位的TCP首部长度,由于TCP选项字段的原因,该TCP首部的长度是可变的。

  • 可选与变长的选项字段,该字段用于发送方与接收方协商最大报文段长度 (MSS) 时,或在高速网络环境下用作窗口调节因子时使用。

  • 6 位的标志字段,各个标志作用简介如下

    • URG:紧急指针有效
    • ACK:该位为 1 时,「确认应答」的字段变为有效,TCP 规定除了最初建⽴连接时的 SYN 包之外该位必须设置为 1 。
    • PSH:接收方应该尽快将这个报文段交给应用层。
    • RST:该位为 1 时,表示 TCP 连接中出现异常必须强制断开连接。
    • SYN:该位为 1 时,表示希望建⽴连接,并在其「序列号」的字段进⾏序列号初始值的设定。
    • FIN:该位为 1 时,表示今后不会再有数据发送,希望断开连接。当通信结束希望断开连接时,通信双⽅的主机之间就可以相互交换 FIN 位为 1 的 TCP 段。
  • 16 位的紧急数据指针字段,当紧急数据存在并给出指向紧急数据尾的指针的时候, TCP 必须通知接收端的上层实体 。

序号和确认号

  • 序号

    • TCP 把数据看成一个无结构的、有序的字节流。
    • 序号是按顺序给发送数据的每一个字节都标上编号。
    • 例如数据流由一个500 000字节的文件组成,MSS为1000字节,数流的首字节编号是0,依次每个字节+1,并分成500个报文段。(序列号的初始值并非为0。而是在建立连接以后由随机数生成。而后面的计算则是对每一字节加一。)
    • 序号的主要作用概括:
      • 接收方可以去除重复的数据;
      • 接收方可以根据序号按序接收;
      • 可以标识发送出去的数据包中, 哪些是已经被对方收到;
  • 确认号

    • 在TCP中,当发送端的数据到达接收主机时,接收端主机会返回一个已收到消息的通知。这个消息叫做确认应答(ACK)。
    • 结合上序号,主机会在确认号字段填上已经接受到的字节序号,下次发送就从该序号开始。
    • 例如,要发送序号0~1000的报文段,而接受端只接受到0 ~ 535 的报文段以及900 ~ 1000的报文段,那么发送的确认号就会加上536的序号,下次发送就会从536开始发送。

3.3 TCP 连接管理

3.3.1 三次握手

客户端的TCP 会通过三次握手的方式来和服务器中的TCP建立一条连接。

在这里插入图片描述

  • 第一次握手
    • 客户端会随机初始化序号( client_isn ),将此序号置于 TCP ⾸部的「序号」字段中,同时把 SYN 标志位置为 1 ,称为 SYN 报⽂段。
    • 这个报文段会被封装在一个IP数据报中,并发送给服务器,表示向服务端发起连接,该报文段不包含应⽤层数据,之后客户端处于 SYN-SENT 状态。
  • 第二次握手
    • 服务端收到客户端的 SYN 报文段后,会为该连接分配TCP缓存和变量,并随机初始化自己的序号( server_isn ),将此序号填⼊TCP 首部的「序号」字段中,其次把 TCP 首部的「确认应答号」字段填⼊ client_isn + 1 , 接着把 SYN 和 ACK 标志位置为 1 。该允许连接的报文段称为SYNACK报文段
    • 最后把该报⽂发给客户端,该报⽂也不包含应⽤层数据,之后服务端处于 SYN-RCVD 状态。
  • 第三次握手
    • 客户端收到SYNACK报文段之后,客户端也要给该连接分配缓存和变量,还要向服务端回应最后⼀个应答报文,首先该应答报文 TCP ⾸部 ACK 标志位置为1 ,其次「确认应答号」字段填⼊ server_isn + 1 ,最后把报文发送给服务端。
    • 这次报文段可以携带客户端到服务器的数据(只有在第三次握手才能携带数据),之后客户端处于 ESTABLISHED 状态。服务器收到客户端的应答报⽂后,也进⼊ ESTABLISHED 状态。
为什么不是两次握手,或者四次?

原因⼀:避免旧连接造成混乱和资源浪费

RFC 793 指出:
The principle reason for the three-way handshake is 
to prevent old duplicate connection initiations from causing confusion.

说明三次握手的首要原因是为了防止旧的重复连接初始化造成混乱。

例如由于网络延迟,客户端多次发送SYN报文段建立连接的情况

  • 如果,一个旧的报文段先于一个新的报文段到达服务端,
  • 这时服务端会返回一个SYNACK报文段,这个报文段内将旧的 client_isn 序号+1。
  • 客户端收到后可以根据⾃身的上下文,判断这是⼀个历史连接(因为发送回的确认号不是发送出去的序号+1),那么客户端就会发送RST 报文给服务端,表示出现异常强制中止这⼀次连接。

如果是两次握手连接,就不能判断当前连接是否是历史连接,三次握手则可以在客户端准备发送第三次报文时,判断该连接的合法性。

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

由于没有第三次握手,服务器不清楚客户端是否收到了⾃⼰发送的建立连接的 ACK 确认信号,所以每收到⼀个 SYN 报文段,就只能先主动建立一个连接。

如果客户端的 SYN 报文段阻塞了,重复发送多次 SYN 报文段,那么服务器在收到请求后就会建⽴多个冗余的无效链接,造成不必要的资源浪费。

原因二:同步双方初始序号

TCP 协议的通信双⽅, 都必须维护⼀个「序号」,因为TCP是一个全双工的服务, 序列号是可靠传输的⼀个关键因素。

以当客户端发送携带「初始序号」的 SYN 报文段的时候,需要服务端回⼀个 SYNACK 应答报文段,表示客户端的 SYN 报文段已被服务端成功接收,当服务端发送「初始序号」给客户端的时候,依然也要得到客户端的应答回应,这样两边的发送和回复才能确保双方的初始序列号能被可靠的同步。

3.3.2 SYN 洪泛攻击

在 TCP 三次握手中,服务器为了响应一个收到的 SYN ,分配并初始化连接变量和缓存,然后服务器发送一个 SYNACK 进行响应,并等待来自客户的 ACK 报文段 如果某客户不发送 ACK 来完成该三次握手,最终(通常在一分多钟之后)服务器将终止该半开连接并回收资源。

这种 TCP 连接管理协议为经典的 DoS 攻击 SYN 洪泛攻击 (SYN flood attack) 提供了环境,在这种攻击中,攻击者发送大量的 TCP SYN 报文段,而不完成第三次握手的步随着这种 SYN 报文段纷至沓来,服务器不断为这些半开连接分配资源(但从未使用) ,导致服务器的连接资源被消耗殆尽。

现在有一种有效的防御系统,成为 SYN cookie。在Linux中有如下参数配置。

net.ipv4.tcp_syncookies = 1

DDOS 攻击是一大类攻击的总和,目的在于使用大量的请求,耗尽服务器的资源,导致停止服务或者实质下线。

SYN cookie 通过如下方式工作:

  • 当服务器接收到一个 SYN 报文段时,它并不知道该报文段是来自一个合法的用户,还是一个 SYN 洪泛攻击的一部分 因此服务器不会为该报文段生成一个半开连接。
  • 服务器会生成一个初始 TCP 序列号,该序列号是 SYN 报文段的源和目的 IP 地址与端口号以及仅有该服务器知道的秘密数的一个散列函数,称为cookie。服务器放松该特殊序列号作为序号的SYNACK报文段作为回应。另外,服务也并不对cookie 或者 SYN信息进行记忆。
  • 如果客户端是合法的,回返回一个ACK报文段,如果该报文段的确认号等于用同样数据进行同样散列函数计算出的cookie值 + 1 ,那么该客户端就对应了之前的 SYN 报文段,则建立全开连接。
  • 如果客户端没有返回一个 ACK 报文段,则初始的 SYN 并没有对服务器产生危害,因为服务器没有为它分配任何资源。

3.3.3 四次挥手

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

为什么挥手需要四次?

因为服务器端在发送 ACK 确认报文段之后,通常需要等待完成数据的发送和处理,才能发送关闭连接的 FIN 报文段,所以这部分需要分成两个报文段来发,就比三次握手多了一次。

为什么需要最后的 TIME_WAIT 状态?

  • ① 防⽌旧连接的数据包
    • 如果没有等待时间,又有新的TCP连接复用了刚才使用的端口,服务器端没有收到ACK后重发 FIN 报文段,发送给了新的客户端,造成错乱。
  • ② 保证双方的连接都可以正常关闭
    • 服务端正常收到四次挥⼿的最后⼀个 ACK 报文段,则服务端正常关闭连接。
    • 服务端没有收到四次挥⼿的最后⼀个 ACK 报文段时,则会重发 FIN 关闭连接报⽂并等待新的 ACK 报⽂。

为什么 TIME_WAIT 等待的时间是 2MSL?

MSL 是报文段最大生存时间(Maximum Segment Lifetime)。它是任何报文段被丢弃前在网络内的最长时间。

TIME_WAIT 的时间是 2MSL 因为客户端接收到 FIN 报文段之后发送 ACK 确认报文段,如果服务端没有接收到发过来的ACK,就会重发 FIN 报文段,所以发送ACK 的时间和对方发送 FIN 的时间刚好是2个MSL,如果超过2个MSL没有再次收到 FIN 说明服务器端已经断开连接。如果客户端又接收到了服务端重发的 FIN 报文,那么 2MSL 时间将重新计时。

在 Linux 系统⾥ 2MSL 默认是 60 秒,那么⼀个 MSL 也就是 30 秒。

#define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to destroy TIME-WAIT
									 state, about 60 seconds */

TIME_WAIT 过多是为什么,有什么危害,如何优化?

因为客户端建立大量短连接,造成大量的TIME_WAIT。

危害是: 占用系统资源,一个 TIME_WAIT 也就是一个连接的文件描述符会占用 4kb;

并不会占用端口。

解决:

  • 打开 net.ipv4.tcp_tw_reuse 和 net.ipv4.tcp_timestamps 选项;可以快速复用 TIME_WAIT 的套接字,打开时间戳支持,将过期的报文段丢弃,防止旧的报文段导致错误。
  • 使用 SO_LINGER 直接用 RST 报文强制关闭连接。

对于TIME_WAIT 的快速回收和复用,快速回收是有害的,之后建立的连接可能会被之前迟到的数据包引起异常而关闭,或者被之前的连接劫持。复用是无害的,因为引入了时间戳,不会被之前的连接困扰。

那 CLOSE_WAIT 过多是为什么,有什么危害,如何优化?

如果服务端有大量的close-wait的连接没有及时关闭,而客户端又不断地发送新的连接请求,这样就会打开的文件描述符数会不断增加,表现为 CPU 或者 内存飙高。在linux系统中,一个进程最大可以同时打开的文件描述符是有上限的,ulimit命令可以查到。当达到这个上限时,服务端进程将无法新建socket来响应新的请求。

CLOSE_WAIT 过多表示服务器在接收到关闭请求后,没有立马的做出相应并关闭请求,会导致连接无法释放占用文件描述符资源,问题一定出现在程序内部,而不是通过调操作系统的参数解决。

3.4 TCP 超时与重传

3.4.1 超时重传

超时重传就是在发送数据时,设定⼀个定时器,当超过指定的时间后,没有收到对方的 ACK 确认应答报文段,就会重发该数据。

TCP要求不论处在何种网络环境下都要提供高性能通信,并且无论网络拥堵情况发生何种变化,都必须保持这一特性。为此,它在每次发包时都会计算往返时间(Round Trip Time也叫 RTT。是指报文段的往返时间。) 及其偏差(RTT时间波动的值、方差。有时也叫抖动。)。

最理想的情况是,找到一个最小时间,它能保证确认应答一定能在这个时间内返回。所以超时重传时间 的值应该略大于报文段的往返 RTT 的值。

由于路由器的拥塞和端系统负载的变化,「报文段往返时间」是经常波动变化的,所以「超时重传时间」也应该是⼀个动态变化的值。

RTT根据如下公式计算:

在这里插入图片描述

  • 第一个公式计算RTT的平均时间,每当有一个新的样本RTT,就会更新该值。
  • 第二个公式计算估算每次RTT和平均RTT的偏差,也会不断更新。
  • 第三个公式计算应该设置的超时时间。
  • 另外每当遇到⼀次超时重传的时候,都会将下⼀次超时时间间隔设为先前值的两倍。两次超时,就说明网络环境差,不宜频繁反复发送。

3.4.2 快速重传

快速重传指的是当收到三个相同的 ACK 报文段时,就重传丢失的数据报文段,而无需等待超时定时器溢出。

例如,A向B发送序号1~5 的报文段,1 顺利到达,而2没有到达,345都顺利到达,但回复的ACK 应答报文段都是序号2,则说明序号2丢失了需要重传。

但是快速重传只解决了时间问题,但是在重传的时候,并不知道需要重传哪些,是2,还是2345 ?

3.4.3 SACK 选项

要在 TCP 头部「选项」字段⾥加⼀个 SACK 的选项,它可以将接受缓冲区的地图发送给发送⽅,这样发送方就可以知道哪些数据收到了,哪些数据没收到,知道了这些信息,就可以只重传丢失的数据。(ACK表示下一个需要接受的序号,SACK表示之后已经接收到的范围)

为了更好的反应网络情况,RFC 2883在SACK选项的基础上提出了D-SACK(即Duplicate SACK)。接收方收到的乱序报文中同样有可能是会出现重复段,在SACK选项的第一个块中携带该重复段的序号,该序号可能是已经确认过的(小于ACK序号),或者大于其后面其它SACK的序号,发送方可以根据第一个块更加精细的判断网络状况:如数据段被复制、错误重传等。

3.5 滑动窗口

TCP以1个段为单位,每发一个段进行一次确认应答的处理,这样的传输方式有一个缺点。那就是,包的往返时间越长通信性能就越低。为解决这个问题,TCP 引⼊了窗⼝这个概念。即使在往返时间较⻓的情况下,它也不会降低⽹络通信的效率。这个机制实现了使用大量的缓冲区(缓冲区(Buffer)在此处表示
临时保存收发数据的场所。通常是在计算机内存中开辟的一部分空间。) ,通过对多个段同时进行确认应答的功能。

TCP 头⾥有⼀个字段叫 Window ,也就是窗口大小。这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能⼒来发送数据,⽽不会导致接收端处理不过来。所以,通常窗口大小是由接收方的窗口大小来决定的。

滑动窗口其实是一个抽象的概念,指的是在接收到一部分报文段之后好像向后滑动一样接受下一段消息。

  • 发送方滑动窗口

    • 在这里插入图片描述

    • 之后接受方确认一个字节,滑动窗口就会向后滑动一个。

    • 滑动窗口内没有发送的也会继续发送,如果已经发送的出现了丢包等,就会触发重传机制。

  • 接收方滑动窗口

    • 在这里插入图片描述
    • 只有中间部分队列头的数据接收到并且返回ACK应答报文段,窗口才会向后滑动。例如,2没有收到,但是3456都收到了,也不会向后滑动,并且会返回2的ACK应答。

3.6 流量控制

如果发送方只根据自己的情况发送数据给接收端,而接收端在一些情况下无法处理,将本应该接收的数据丢弃的话,就又会触发重发机制,从而导致网络流量的无端浪费。为了防止这种现象的发生,TCP提供一种机制可以让发送端根据接收端的实际接收能力控制发送的数据量。这就是所谓的流量控制。

它的具体操作是,接收端主机向发送端主机通知自己可以接收数据的大小,于是发送端会发送不超过这个限度的数据。该大小限度就被称作窗口大小。不过,接收端的这个缓冲区一旦面临数据溢出时,窗口大小的值也会随之被设置为一个更小的值通知给发送端,从而控制数据发送量。

窗口关闭

窗口关闭指的是如果窗口大小为 0 时,就会阻止发送方给接收方传递数据,直到窗口变为非 0 为止,因为接收方是通过ACK报文来通知窗口大小的,如果窗口关闭之后,接收方处理完数据,又可以接受新的数据,发送的窗口非 0 的ACK在网络中丢失,这样就产生死锁,发送方以为接收方无法接收新的数据,而接收方以为发送方还没有发数据。

为了解决这个问题,TCP 为每个连接设有⼀个持续定时器,只要 TCP 连接⼀方收到对⽅的零窗口通知,就启动持续计时器。如果持续计时器超时,就会发送窗口探测 ( Window probe ) 报问,而对方在确认这个探测报文时,给出自己现在的接收窗口大小。

  • 如果仍然是0,则重置计时器。
  • 如果不是0,则开始正常的发送,打破僵局。

糊涂窗口综合症

糊涂窗口综合症指的是:接收方可以通告一个小的窗口(而不是一直等到有大的窗口时才通告),而发送方也可以发送少量的数据(而不是等待其他的数据以便发送一个大的报文段)。双方可能就是几个字节几个字节的交互,而TCP和IP的首部,可能就有40个字节,所以成本太高。

解决的方法就是解决接收方通告小窗口和发送方发送少量数据的问题。

  • 让接收方不通告小窗口
    • 当「窗口大小」小于 MSS(最大报文端长度) 与 1/2 缓存大小中较小值时,就会向发送方通告窗口为 0 ,也就阻止了发送⽅再发数据过来。
    • 当接收方处理一些数据,窗口大小变大以后,再重新接收数据。
  • 发送方不发送少量数据
    • 发送数据满足以下条件
      • 发送一个满长度的报文段;
      • 发送至少是接收方通告窗口大小一半的报文段;
    • 介绍一下Nagle算法
      • 该算法要求一个 TCP连接上最多只能有一个未被确认的未完成的小分组,在该分组的确认到达之前不能发送其他的小分组。
      • 另外,TCP收集这些少量的分组,并在确认到来时以一个分组的方式发出去。
    • 利用该算法显然可以避免发送少量数据。

3.7 拥塞控制

流量控制解决了两个进程之间发送数据的控制,而计算机网络都处在一个共享的环境。因此也有可能会因为其他主机之间的通信使得网络拥堵。在网络出现拥堵时,如果两个进程突然发送较大量的数据,极有可能会导致整个网络的瘫痪。所以就有了拥塞控制,防止发送方突然发送大量的数据。

为了在发送端调节所要发送数据的量,定义了一个叫做“拥塞窗口”的概念,拥塞窗口cwnd 是发送方维护的⼀个的状态变量,它会根据网络的拥塞程度动态变化,另外发送窗口是拥塞窗口和接受窗口较小的值

  • 如果网络中没有出现拥塞,拥塞窗口就会增⼤;
  • 网络中出现了拥塞,拥塞窗口就会变小;

① 慢启动

慢启动指的是TCP在刚建立连接的时候,将拥塞窗口设置的比较小,控制发送方的发送数据报数量,之后再视情况慢慢增加。

  • 一般将拥塞窗口初始化为 1 MSS(最大报文长度)。
  • 之后当发送方每收到⼀个 ACK 应答报文段,拥塞窗口 cwnd 的大小就会加 1个报文段(cwnd以字节为单位,但是慢启动以报文段大小为单位进行增加)。
  • 随着包的每次往返,拥塞窗口也会以指数函数的增长,拥堵状况激增甚至导致网络拥塞的发生。为了防止这些,引入了慢启动阀值的概念,会维护一个变量 ssthresh 慢启动阈值,通常为 65535 个字节。

② 拥塞避免

  • 拥塞避免算法要求每次收到一个确认时将 cwnd 增加1 / cwnd。与慢启动的指数增加比起来,这是一种加性增长(additive increase)。
  • 可以把拥塞窗口指数的增长趋近与线性增长。

③ 拥塞发生

拥塞发生可能是超时或者收到重复 ACK 应答报文段开始的。

  • 超时重传:如果是超时重传引起的拥塞
    • ssthresh 会设置为 cwnd/2 。
    • cwnd 会重置为 1,重新进行慢启动。并当之后 cwnd 超过 ssthresh 之后,会进行拥塞避免。
    • 这种反应很强烈会突然造成网络卡顿。
  • 快速重传:如果是接收到了三个相同的 ACK 应答报文段,出发快速重传,说明只丢了中间的一部分。这时候会执行快速恢复

④ 快速恢复

  • 将 ssthresh 设置为 cwnd 的一半。拥塞窗口 cwnd 设置为 ssthresh + 3。
  • 重传丢失的数据包。
  • 如果再收到重复的 ACK,那么 cwnd 继续增加 1 报文段。
  • 如果收到新数据的 ACK 后,说明之前的数据都已经收到,则把 cwnd 设置为第⼀步中的 ssthresh 的值(就是那时候 cwnd 的一半),然后进入拥塞避免状态。

4. TCP 和 UDP 的区别

  • 连接方面
    • TCP 是面向连接的传输层协议,传输数据前先要建立连接。
    • UDP 是不需要连接的,封装成UDP报文会立刻传递给网络层。
  • 数据可靠性方面
    • TCP 是可靠交付数据的,数据可以无差错、不丢失、不重复、按需到达。
    • UDP 是尽最⼤努力交付,不保证可靠交付数据。
  • 提供服务的对象
    • TCP 是⼀对⼀的两点服务,即⼀条连接对于了两个进程的通信。
    • UDP ⽀持⼀对⼀、⼀对多、多对多的交互通信。
  • 流量控制,拥塞控制
    • TCP 有拥塞控制和流ᰁ控制机制,保证数据传输的安全性。
    • UDP 则没有,只管消息的传递。
  • 首部开销
    • TCP 首部长度长,首部在没有使用选项字段时是 20 个字节,如果使用了选项字段则会变⻓的。
    • UDP 首部只有 8 个字节,并且是固定不变的。
  • 传输方式
    • TCP 是流式传输,没有边界,但保证顺序和可靠。
    • UDP 是⼀个包⼀个包的发送,是有边界的,但可能会丢包和乱序。
  • 分片方式不同
    • TCP 的数据大小如果大于 MSS ,则会在传输层进行分片,目标主机收到后,也同样在传输层组装 TCP数据包,如果中途丢失了⼀个分片,只需要传输丢失的这个分片。
    • UDP 的数据大小如果大于 MTU ,则会在 IP 层进行分片,⽬标主机收到后,在 IP 层组装完数据,接着再传给传输层,但是如果中途丢了⼀个分片,在实现可靠传输的 UDP 时则就需要重传所有的数据包。这样显然效率十分低下,所以UDP 发送的数据报应该小于MUT。

5. 总结

TCP 内容真多!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值