计算机网络-运输层
注:内容根据《计算机网络第七版》以及相关PPT整理制作,图片主要源于{电子工程出版社},部分图片源于学校老师上课所画的图以及自己制作的思维导图
概述
运输层主要是面向通信部分的最高层,同时也是用户功能中的最低层。主要有以下两点
- 从通信和信息处理的角度看,运输层向它上面的应用层提供通信服务。
- 当网络的边缘部分中的两个主机使用网络的核心部分的功能进行端到端的通信时,只有位于网络边缘部分的主机的协议栈才有运输层,而网络核心部分中的路由器在转发分组时都只用到下三层的功能。
逻辑通信
运输层为相互通信的应用进程提供逻辑通信
- 逻辑通信:好像是这样通信,但事实上并非真的这样通信
通过以下图结合来理解,在平行上,应用进程之间是通过运输层进行通信的(逻辑通信),但实际上数据的传递依旧还是要通过下一层网络层及下下层再通过路由器进行传递的
这里举个形象的栗子:你要写信给你的盆友,你只要记得门口邮箱在哪个位置,然后投进你的信件,而不需要考虑它是怎样进行运输从而到达盆友门口邮箱里的,盆友也只用直接从他家门口邮箱里取即可,这里的邮箱好比就是运输层,你们的交流就好像是通过邮箱(逻辑通信)进行的,但实际上邮件还是依然要通过多个运输环节(五层协议)才能到达盆友家。
屏蔽作用
运输层向高层用户屏蔽了下面网络核心的细节,它使应用进程看见的就是好像在两个运输层实体之间有一条端到端的逻辑通信信道。
正因为前面的逻辑通信,所以我们可以专注于运输层的两个进程通信问题,而不必去关心底层的实现
从IP层来说,通信的两端是两台主机。
从运输层的角度看,通信的真正端点并不是主机而是主机中的进程。也就是说,端到端的通信是应用进程之间的通信。
端系统之间通信的含义:运行在主机A上的某个程序和运行在主机B上的另一个程序进行通信”。端到端的通信是进程之间的通信
复用与分用
复用:多个应用层进程汇聚成一个传输层进程
分用:传输层的单个进程通向多个应用层进程
形象来说:复用就是多车道变成单车道运输层,分用就是单车道分多车道通向多个应用进程(UDP会涉及到)
TCP与UDP
- 当运输层采用面向连接的 TCP 协议时,尽管下面的网络是不可靠的(只提供尽最大努力服务),但这种逻辑通信信道就相当于一条全双工的可靠信道。
- 当运输层采用无连接的 UDP 协议时,这种逻辑通信信道是一条不可靠信道。
UDP特点
- 无连接的协议,提供无连接服务;
- 其传送的运输协议数据单元TPDU是UDP报文或用户数据报;
- 支持单播、多播、厂播;
- 不提供可靠交付;
- 简单。适用于很多应用,如: 多媒体应用等。
TCP特点
- 面向连接的协议,提供面向连接服务;
- 其传送的运输协议数据单元TPDU是TCP报文;
- 支持点对点单播,不支持多播、广播;
- 提供可靠服务;
- 复杂,用于大多数应用,如:万维网、电子邮件、文件传送等。
各自的应用范围
其中动态主机配置DHCP是在应用层ip地址动态分配时的相关应用,HTTP协议是客户端与服务器端交互的相关规则与约定
IP数据报、TCP和UDP的关系
- 运输层的 UDP 用户数据报与网际层的IP数据报有很大区别。
- IP 数据报要经过互连网中许多路由器的存储转发。
- UDP 用户数据报是在运输层的端到端抽象的逻辑信道中传送的。
- TCP 报文段是在运输层抽象的端到端逻辑信道中传送,这种信道是可靠的全双工信道。但这样的信道却不知道究竟经过了哪些路由器,而这些路由器也根本不知道上面的运输层是否建立了 TCP 连接。
端口
运行在计算机中的进程是用进程标识符来标志的。
为什么会有端口
- 由于进程的创建和撤销都是动态的,发送方几乎无法识别其他机器上的进程。
- 有时我们会改换接收报文的进程,但并不需要通知所有发送方。
- 我们往往需要利用目的主机提供的功能来识别终点,而不需要知道实现这个功能的进程。
虽然通信的终点是应用进程,但我们可以把端口想象是通信的终点,因为我们只要把要传送的报文交到目的主机的某一个合适的目的端口,剩下的工作(即最后交付目的进程)就由 TCP 来完成。
形象地说:ip地址更像是标识你家在哪栋楼,端口更像是标识你是哪间房
硬件端口和软件端口
- 在协议栈层间的抽象的协议端口是软件端口。
- 路由器或交换机上的端口是硬件端口。
也就是说:硬件端口是 用来连接各个硬件设备的,而软件端口则是应用层的各种协议进程与运输实体进行层间交互的一种地址
端口位数
端口用一个 16 位端口号进行标志,允许有65,535(2¹⁶)个不同的端口号
端口号只具有本地意义,即端口号只是为了标志本计算机应用层中的各进程。在互联网中,不同计算机的相同端口号是没有联系的。
由此可见,两个计算机中的进程要互相通信,不仅必须知道对方的端口号(为了找到对方计算机中的应用进程) ,而且还要知道对方的 IP 地址(为了找到对方的计算机)。
常见端口
- 服务器端使用的端口号
- 熟知端口,数值一般为 0 ~ 1023。
- 登记端口号,数值为 1024 ~ 49151,为没有熟知端口号的应用程序使用的。使用这个范围的端口号必须在 IANA 登记,以防止重复。
- 客户端使用的端口号
- 又称为短暂端口号,数值为 49152 ~ 65535,留给客户进程选择暂时使用。
- 当服务器进程收到客户进程的报文时,就知道了客户进程所使用的动态端口号。通信结束后,这个端口号可供其他客户进程以后使用。
用户数据报协议UDP
UDP 只在 IP 的数据报服务之上增加了很少一点的功能
- 复用和分用的功能
- 差错检测的功能
前面已经简单的介绍了UDP的特点了,这里补充其它的特点以及这些特点的优点
- UDP 是无连接的,发送数据之前不需要建立连接,,因此减少了开销和发送数据之前的时延。
- UDP 使用尽最大努力交付,即不保证可靠交付,因此主机不需要维持复杂的连接状态表。
- UDP 是面向报文的。UDP 对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。UDP 一次交付一个完整的报文。
- UDP 没有拥塞控制,因此网络出现的拥塞不会使源主机的发送速率降低。这对某些实时应用是很重要的。很适合多媒体通信的要求。
- UDP 支持一对一、一对多、多对一和多对多的交互通信。
- UDP 的首部开销小,只有 8 个字节,比 TCP 的 20 个字节的首部要短。
再来看看UDP的这两个特点
- 发送方 UDP 对应用程序交下来的报文,在添加首部后就向下交付 IP 层。UDP 对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。
- 应用层交给 UDP 多长的报文,UDP 就照样发送,即一次发送一个报文。
这意味着什么,接收方 UDP 对 IP 层交上来的 UDP 用户数据报,在去除首部后就原封不动地交付上层的应用进程,一次交付一个完整的报文。
所以如果你的报文太长,UDP把它交给IP层后,IP层再传送时就可能要分片,这会降低IP层的效率,而如果报文太短,则IP数据报的首部相对于报文会太长,同样也会降低IP层效率,所以应用程序必须选择合适的报文长度
UDP的首部格式
用户数据报 UDP 有两个字段:数据字段和首部字段。
首部字段有 8 个字节,由 4 个字段组成,每个字段都是 2 个字节
在计算检验和时,临时把 12 字节的“伪首部”和 UDP 用户数据报连接在一起。伪首部仅仅是为了计算检验和
计算检验和这里不展开说(因为没去算),感兴趣可以通过书本理解
传输控制协议TCP概述
TCP 是面向连接的运输层协议,在无连接的、不可靠的 IP 网络服务基础之上提供可靠交付的服务。为此,在 IP 的数据报服务基础之上,增加了保证可靠性的一系列措施。
同样以复用和分用进行通信
TCP 最主要的特点
- TCP 是面向连接的运输层协议。
- 每一条 TCP 连接只能有两个端点 (endpoint),每一条 TCP 连接只能是点对点的(一对一)。
- TCP 提供可靠交付的服务。
- TCP 提供全双工通信。
- 面向字节流
- TCP 中的“流”(stream) 指的是流入或流出进程的字节序列。
- “面向字节流”的含义是:虽然应用程序和 TCP 的交互是一次一个数据块,但 TCP 把应用程序交下来的数据看成仅仅是一连串无结构的字节流。
TCP面向流的具体说明
TCP 不保证接收方应用程序所收到的数据块和发送方应用程序所发出的数据块具有对应大小的关系。
但接收方应用程序收到的字节流必须和发送方应用程序发出的字节流完全一样。
注意:
- TCP 连接是一条虚连接而不是一条真正的物理连接。
- TCP 对应用进程一次把多长的报文发送到 TCP 的缓存中是不关心的。
- TCP 根据对方给出的窗口值(流量控制)和当前网络拥塞(拥塞控制)的程度来决定一个报文段应包含多少个字节(UDP 发送的报文长度是应用进程给出的)。
- TCP 可把太长的数据块划分短一些再传送。
- TCP 也可等待积累有足够多的字节后再构成报文段发送出去。
后面几节会具体展示其是如何工作的(可靠传输),以及这些关键点的实现
TCP的连接(套接字)
- TCP 把连接作为最基本的抽象。
- 每一条 TCP 连接有两个端点。
- TCP 连接的端点不是主机,不是主机的IP 地址,不是应用进程,也不是运输层的协议端口。TCP 连接的端点叫做套接字 (socket) 或插口。
- 端口号拼接到 (contatenated with) IP 地址即构成了套接字。
下面通过图理解套接字
套接字的格式:套接字 socket = (IP地址 : 端口号)
例子:套接字 socket = (192.169.1.20 : 2028)
每一条 TCP 连接唯一地被通信两端的两个端点(即两个套接字)所确定。
根据以上可以得出
- TCP 连接就是由协议软件所提供的一种抽象。
- TCP 连接的端点是个很抽象的套接字,即(IP 地址:端口号)。
- 同一个 IP 地址可以有多个不同的 TCP 连接。
- 同一个端口号也可以出现在多个不同的 TCP 连接中。
Socket其实有多种含义,在不同的场景其含义不同,比如
- 应用编程接口 API 称为 socket API, 简称为 socket。
- socket API 中使用的一个函数名也叫作 socket。
- 调用 socket 函数的端点称为 socket。
- 调用 socket 函数时其返回值称为 socket 描述符,可简称为 socket。
- 在操作系统内核中连网协议的 Berkeley 实现,称为 socket 实现
可靠传输的工作原理
直接上图——TCP也就是建立在IP协议不可靠传输上的可靠传输(前面有讲过)
让我们试想一下:我们希望的传输是不会出差错的,也就是不论发送何种文件,你都希望它能稳定可靠的传输到目的地去,以及接收方能准时无误的接收到你传送的文件,不希望它在某个过程中出现差错,以致于你要重新去传输该文件,所以理想传输总结起来有两点:
- 传输信道不产生差错。
- 不管发送方以多快的速度发送数据,接收方总是来得及处理收到的数据。
然而实际上网络并非如此,它都不具备以上两个理想条件,所以我们必须使用一些可靠传输协议(TCP),在不可靠的传输信道(IP)实现可靠传输
停止等待协议
“停止等待”就是每发送完一个分组就停止发送,等待对方的确认。在收到确认后再发送下一个分组。
我们知道:TCP是提供全双工工作方式的,也就是通信双方即是发送方也可以是接收方,但是这里为了方便,只考虑单方向(双方向工作原理一致)。定为A→发送方,B→接收方
A 发送分组 M1,发完就暂停发送,等待 B 的确认 (ACK)。B 收到了 M1 向 A 发送 ACK。A 在收到了对 M1 的确认后,就再发送下一个分组 M2。如此循环反复下去。
那么问题来了,出错了咋办,让我们先来看看接收方B在传输过程可能会出现的错误
在接收方 B 会出现两种情况:
- B 接收 M1 时检测出了差错,就丢弃 M1,其他什么也不做(不通知 A 收到有差错的分组)。
- M1 在传输过程中丢失了,这时 B 当然什么都不知道,也什么都不做。
在这两种情况下,B 都不会发送任何信息。但A都必须重发分组,直到B正确接收为止,这样才能实现可靠通信。那么问题又来了,A怎么知道自己是否该重传分组了呢,没错,超时重传
超时重传(自动重传请求 ARQ)
- A 为每一个已发送的分组都设置了一个超时计时器。
- A 只要在超时计时器到期之前收到了相应的确认,就撤销该超时计时器,继续发送下一个分组 M2 。
- 若A在超时计时器规定时间内没有收到B的确认,就认为分组错误或丢失,就重发该分组。
编号
接下来,看看超时重传会遇到的问题
若分组正确到达B,但B回送的确认丢失或延迟了,A未收到B的确认,会超时重发。B 可能会收到重复的 M1 。B如何知道收到了重复的分组,需要丢弃呢?
解决方法便是“编号”
- A为每一个发送的分组都进行编号。若B收到了编号相同的分组,则认为收到了重复分组,丢弃重复的分组,并回送确认。
- B为发送的确认也进行编号,指示该确认是对哪一个分组的确认。
- A根据确认及其编号,可以确定它是对哪一个分组的确认,避免重发发送。若为重复的确认,则将其丢弃。
确认丢失和确认迟到
好滴,又有问题了(你这问题怎么这么多!!!)
如果接收方Bd的确认在路上直接丢失了呢(确认丢失)
又或者
传输过程中没有出现差错,但 B 对分组 M1 的确认迷路了,走了点弯路,然后迟到了呢(确认迟到)
让我们先来解决第一个问题
首先,A会超时重发,然后收到确认,这没啥可说的,重点是B,接收方B会收到两次分组,那么它会怎么做,是的,丢掉当前这一个分组并重传确认分组。为什么要重传确认,B说它之前不是发过一次了吗,当然要发,它不能认为已经发送过确认就不再发送,因为 A 之所以重传 M1 就表示 A 没有收到对 M1 的确认。
第二个问题的解决
首先,A收到了B延迟的M1确认,这时,它已经将缓冲的备份分组发出去了,那么当他收到第二次重复的确认时,它会丢弃重复的确认
而B也仍然会收到重复的 M1,它同样会丢弃重复的 M1,并重传确认分组。
最后,用一个图来理解这两个概念
注意
- 在发送完一个分组后,必须暂时保留已发送的分组的副本,以备重发。
- 分组和确认分组都必须进行编号。(区分不同的分组以及确认)
- 超时计时器的重传时间应当比数据在分组传输的平均往返时间更长一些。 (以防迷了点小路而导致重传浪费性能)
总结下停止等待协议的要点
- 停止等待。发送方每次只发送一个分组。在收到确认后再发送下一个分组。
- 编号。对发送的每个分组和确认都进行编号。
- 自动重传请求。发送方为每个发送的分组设置一个超时计时器。若超时计时器超时,发送方会自动重传分组。
- 简单,但信道利用率太低。(信道利用率那一节会讲到该特点以及解决方法)
自动重传请求 ARQ
通过上面的学习:我们可以了解到B不论何时都不需要跟A说分组丢失,要它重传了,因为这些分组该来总会来的(超时重传),A不会欠B的。像上述的这种可靠传输协议常称为自动重传请求 ARQ (Automatic Repeat reQuest)。意思是重传的请求是自动进行的,接收方不需要请求发送方重传某个出错的分组。
- 通常 A 最终总是可以收到对所有发出的分组的确认。如果 A 不断重传分组但总是收不到确认,就说明通信线路太差,不能进行通信。
- 使用上述的确认和重传机制,我们就可以在不可靠的传输网络上实现可靠的通信。
信道利用率
不知道你们有没有发现:每次A发送分组后,需要等待B的确认分组到达后才会发送下次分组,这样的信道利用率是不是会有点低
信道利用率公式
可以看出,当往返时间 RTT 远大于分组发送时间 TD 时,信道的利用率就会非常低。
若出现重传,则对传送有用的数据信息来说,信道的利用率就还要降低。
流水线传输
为了提高传输效率,发送方可以不使用低效率的停止等待协议,而是采用流水线传输。
流水线传输就是发送方可连续发送多个分组,不必每发完一个分组就停顿下来等待对方的确认。这样可使信道上一直有数据不间断地传送。
由于信道上一直有数据不间断地传送,这种传输方式可获得很高的信道利用率。
连续 ARQ协议
基本思想:
- 发送方一次可以发出多个分组。
- 使用滑动窗口协议控制发送方和接收方所能发送和接收的分组的数量和编号。
- 协议规定:每收到一个确认,发送方就把发送窗口向前滑动。
- 接收方一般采用累积确认的方式。
- 采用回退N(Go-Back-N)方法进行重传(后面有解释)。
累积确认
接收方一般采用累积确认的方式。即不必对收到的分组逐个发送确认,而是对按序到达的最后一个分组发送确认,这样就表示:到这个分组为止的所有分组都已正确收到了。
- 优点:容易实现,即使确认丢失也不必重传。
- 缺点:不能向发送方反映出接收方已经正确收到的所有分组的信息。
如果发送方发送了前 5 个分组,而中间的第 3 个分组丢失了。这时接收方只能对前两个分组发出确认。发送方无法知道后面三个分组的下落,而只好把后面的三个分组都再重传一次。
这就叫做 Go-back-N(回退 N),表示需要再退回来重传已发送过的 N 个分组。
可见当通信线路质量不好时,连续 ARQ 协议会带来负面的影响。
TCP 连接的每一端都必须设有两个窗口——一个发送窗口和一个接收窗口。
TCP 的可靠传输机制用字节的序号进行控制。TCP 所有的确认都是基于序号而不是基于报文段。
TCP 两端的四个窗口经常处于动态变化之中。
TCP连接的往返时间 RTT 也不是固定不变的。需要使用特定的算法估算较为合理的重传时间。
滑动窗口协议比较复杂,是 TCP 协议的精髓所在。
发送方维持的发送窗口,它的意义是:位于发送窗口内的分组都可连续发送出去,而不需要等待对方的确认。这样,信道利用率就提高了。
连续 ARQ 协议规定,发送方每收到一个确认,就把发送窗口向前滑动一个分组的位置。
通过图片理解
首先:定义一个发送窗口以及接收窗口
开始发送
接收到正确分组
继续下个窗口
TCP报文段的首部格式
- TCP 虽然是面向字节流的,但 TCP 传送的数据单元却是报文段。
- 一个 TCP 报文段分为首部和数据两部分,而 TCP 的全部功能都体现在它首部中各字段的作用。
- TCP 报文段首部的前 20 个字节是固定的,后面有 4n 字节是根据需要而增加的选项 (n 是整数)。因此 TCP 首部的最小长度是 20 字节。
接下来,对每个部分进行说明
源端口和目的端口字段——各占 2 字节。端口是运输层与应用层的服务接口。运输层的复用和分用功能都要通过端口才能实现。
序号字段seq——占 4 字节。TCP 连接中传送的数据流中的每一个字节都编上一个序号。序号字段的值则指的是本报文段所发送的数据的第一个字节的序号。 为当前端成功发送的数据位数
确认号字段ack——占 4 字节,是期望收到对方的下一个报文段的数据的第一个字节的序号。
若确认号 = N,则表明:到序号N-1为止所有的数据都已正确收到
数据偏移(即首部长度)——占 4 位,它指出 TCP 报文段的数据起始处距离 TCP 报文段的起始处有多远。“数据偏移”的单位是 32 位字(以 4 字节为计算单位)。
保留字段——占 6 位,保留为今后使用,但目前应置为 0。
紧急 URG —— 当 URG = 1 时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据)。
确认 ACK —— 只有当 ACK =1 时确认号字段才有效。当 ACK =0 时,确认号无效。
推送 PSH (PuSH) —— 接收 TCP 收到 PSH = 1 的报文段,就尽快地交付接收应用进程,而不再等到整个缓存都填满了后再向上交付。
复位 RST (ReSeT) —— 当 RST=1 时,表明 TCP 连接中出现严重差错(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立运输连接。
同步 SYN —— 同步 SYN = 1 表示这是一个连接请求(ACK = 0)或连接接受报文(ACK = 1)。
终止 FIN (FINish) —— 用来释放一个连接。FIN=1 表明此报文段的发送端的数据已发送完毕,并要求释放运输连接。
窗口字段 —— 占 2 字节,用来让对方设置发送窗口的依据,单位为字节。
检验和 —— 占 2 字节。检验和字段检验的范围包括首部和数据这两部分。在计算检验和时,要在 TCP 报文段的前面加上 12 字节的伪首部。
在计算检验和时,临时把 12 字节的“伪首部”和 TCP 报文段连接在一起。伪首部仅仅是为了计算检验和。
紧急指针字段 —— 占 16 位,指出在本报文段中紧急数据共有多少个字节(紧急数据放在本报文段数据的最前面)。
选项字段 —— 长度可变。TCP 最初只规定了一种选项,即最大报文段长度 MSS。MSS 告诉对方 TCP:“我的缓存所能接收的报文段的数据字段的最大长度是 MSS 个字节。”
MSS (Maximum Segment Size)
是 TCP 报文段中的数据字段的最大长度。
数据字段加上 TCP 首部才等于整个的 TCP 报文段。
所以,MSS是“TCP 报文段长度减去 TCP 首部长度”。为什么规定MSS
MSS 与接收窗口值没有关系。
若选择较小的 MSS 长度,网络的利用率就降低。
若 TCP 报文段非常长,那么在 IP 层传输时就有可能要分解成多个短数据报片。在终点要把收到的各个短数据报片装配成原来的 TCP 报文段。当传输出错时还要进行重传。这些也都会使开销增大。
因此,MSS 应尽可能大些,只要在 IP 层传输时不需要再分片就行。
但最佳的 MSS 是很难确定的。
其他选项
- 窗口扩大选项 ——占 3 字节,其中有一个字节表示移位值 S。新的窗口值等于 TCP 首部中的窗口位数增大到 (16 + S),相当于把窗口值向左移动 S 位后获得实际的窗口大小。
- 时间戳选项——占 10 字节,其中最主要的字段时间戳值字段(4 字节)和时间戳回送回答字段(4 字节)。
- 选择确认选项——在后面的”TCP可靠传输的实现”介绍。
填充字段 —— 这是为了使整个首部长度是 4 字节的整数倍。
TCP可靠传输的实现
以字节为单位的滑动窗口
前面讲了可靠传输原理中的流水线传输以及连续 ARQ协议以及后面实现的图示,这里通过回顾来补充和引入新知识点
- TCP 使用流水线传输和滑动窗口协议实现高效、可靠的传输。
- TCP 的滑动窗口是以字节为单位的。
- 发送方 A 和接收方 B 分别维持一个发送窗口和一个接收窗口。
- 发送窗口表示:在没有收到确认的情况下,可以连续把窗口内的数据全部发送出去。
- 接收窗口表示:只允许接收落入窗口内的数据。
通过图,可以知道:
- 首先,根据 B 给出的接收窗口值,A 构造出自己的发送窗口。但是不能大于B的窗口,否则可能超出B的接收范围
- 发送窗口表示:在没有收到 B 的确认的情况下,A 可以连续把窗口内的数据都发送出去。
- 发送窗口里面的序号表示允许发送的序号。
- 显然,窗口越大,发送方就可以在收到对方确认之前连续发送更多的数据,因而可能获得更高的传输效率。但其实它还是会收到一些其它因素的影响,比如网络堵塞,后面会讲到
图里讲到TCP标准强烈不建议发送窗口前沿向后收缩,这是为啥嘞,因为在你收缩的时候这些数据可能已经发送出去了,到时候等窗口移过去你又发送一遍,就会发生错误
现在假设A发送了11个字节的数据,但B这边出了点状况,有部分字节没有按序收到,可能是31出错了,导致32、33没有按序收到
这时候B会怎么做,它会等待缺少的数据到来(这里要注意:如同前面讲的,发送方会未确认序号开始一次性把后面的数据发送来,尽管接收方已经接收到了后面的数据。且等新数据到来后接收方依旧会丢弃掉这些重复的数据)
而等到数据正确到来,接收方也准确无误把它们交付给上一层后,窗口就会丢掉这些值,并向前移动
而如果当A 的发送窗口内的序号都已用完,但还没有再收到确认,那么A就必须停止发送。
发送缓存与接收缓存
发送方以及接收方的窗口都会包含缓存,用于存储临时数据
发送方的应用进程会把字节流写入 TCP 的发送缓存。直到接收方正确接收了,它就会把缓存删除。发送缓存存放的数据主要为两种
- 暂存TCP准备发送的数据;
- 暂时存放TCP 已发送出但尚未收到确认的数据。
接收方的应用进程也会从 TCP 的接收缓存中读取字节流。
所以其会暂时存取两种数据
- 按序到达的、但尚未被接收应用程序读取的数据;
- 不按序到达的数据
根据以上,我们需要强调以及知道以下三点
第一,A 的发送窗口并不总是和 B 的接收窗口一样大(因为有一定的时间滞后)。
第二,TCP 标准没有规定对不按序到达的数据应如何处理。通常是先临时存放在接收窗口中,等到字节流中所缺少的字节收到后,再按序交付上层的应用进程。
第三,TCP 要求接收方必须有累积确认的功能,这样可以减小传输开销。
对于接收方:它可以在合适的时候发送确认,也可以在自己有数据要发送时把确认信息顺便捎带上。但请注意两点:
接收方不应过分推迟发送确认,否则会导致发送方不必要的重传,这反而浪费了网络的资源。
捎带确认实际上并不经常发生,因为大多数应用程序很少同时在两个方向上发送数据。
超时重传时间的选择
重传时间的选择是 TCP 最复杂的问题之一。
如果把超时重传时间设置得太短,就会引起很多报文段的不必要的重传,使网络负荷增大。
但若把超时重传时间设置得过长,则又使网络的空闲时间增大,降低了传输效率。
因此TCP 采用了一种自适应算法,它记录一个报文段发出的时间,以及收到相应的确认的时间。这两个时间之差就是报文段的往返时间 RTT。
加权平均往返时间
- TCP保留了RTT的一个加权平均往返时间RTTS(这又称为平滑的往返时间)。
- 第一次测量到 RTT 样本时,RTTS 值就取为所测量到的 RTT 样本值。以后每测量到一个新的 RTT 样本,就按下式重新计算一次 RTTS:
式中,0 <= α < 1。若 α 很接近于零,表示 RTT 值更新较慢。若选择 α 接近于 1,则表示 RTT 值更新较快。
RFC 6298 推荐的 α 值为 1/8,即 0.125。
超时重传时间 RTO
RTO (Retransmission Time-Out) 应略大于上面得出的加权平均往返时间 RTTS。
RFC 6298 建议使用下式计算 RTO:
RTTD 是 RTT 的偏差的加权平均值。
RFC 6298 建议这样计算RTTD 。第一次测量时, RTTD 值取为测量到的 RTT 样本值的一半。在以后的测量中,则使用下式计算加权平均的 RTTD :
β是个小于 1 的系数,其推荐值是 1/4,即 0.25。
往返时间 (RTT) 的测量相当复杂
现在假设这样一种情况,TCP 报文段 1 没有收到确认。重传(即报文段 2)后,收到了确认报文段 ACK。
如何判定此确认报文段是对原来的报文段 1 的确认,还是对重传的报文段 2 的确认?
在计算平均往返时间 RTT 时,只要报文段重传了,就不采用其往返时间样本。
这样得出的加权平均平均往返时间 RTTS 和超时重传时间 RTO 就较准确。
但是,这又引起新的问题。当报文段的时延突然增大了很多时,在原来得出的重传时间内,不会收到确认报文段。于是就重传报文段。但根据 Karn 算法,不考虑重传的报文段的往返时间样本。这样,超时重传时间就无法更新。
修正的 Karn 算法
报文段每重传一次,就把 RTO 增大一些:
系数 λ的典型值是 2 。
当不再发生报文段的重传时,才根据报文段的往返时延更新平均往返时延 RTT 和超时重传时间 RTO 的数值。
实践证明,这种策略较为合理。
选择确认 SACK
前面我们知道,当接收方收到没有正确按序的数据时,发送方会将包括这部分数据也一起发送来,这看起来是不是极其耗费性能,那么我们能否设法只传送缺少的数据而不重传已经正确到达接收方的数据?
选择确认 SACK (Selective ACK) 就是一种可行的处理方法
接收方可以将正确接收到的数据序号发送回发送方,使发送方不要再发送这些数据,这里要注意序号的问题。如图,你接受到了1—1000的数据那么确认号就要在右边界上再加1
如果要使用选择确认,那么在建立 TCP 连接时,就要在 TCP 首部的选项中加上“允许 SACK”的选项,而双方必须都事先商定好。
如果使用选择确认,那么原来首部中的“确认号字段”的用法仍然不变。只是以后在 TCP 报文段的首部中都增加了 SACK 选项,以便报告收到的不连续的字节块的边界。
由于首部选项的长度最多只有 40 字节,而指明一个边界就要用掉 4 字节,因此在选项中最多只能指明 4 个字节块的边界信息。
最后,也是重要的一点
因为SACK文档没有指明发送方应当怎样响应SACK,因此大多数的实现还是重传所有未被确认的数据块
TCP的流量控制
利用滑动窗口实现流量控制
一般说来,我们总是希望数据传输得更快一些。但如果发送方把数据发送得过快,接收方就可能来不及接收,这就会造成数据的丢失。
流量控制 (flow control) 就是让发送方的发送速率不要太快,既要让接收方来得及接收,也不要使网络发生拥塞。
利用滑动窗口机制可以很方便地在 TCP 连接上实现流量控制。
用一个图来表示这个过程
这便是通过滑动窗口机制来来同台控制数据流量
但是,有这么一个情况,当B不允许A发送过后一段时间内,需要重新进行数据传输,但是巧了,这时候B的请求在路上丢失了,咋办,这个互不搭理(停止传输数据)的状态不就会这样一直保持下去吗
这种互相等待的状态也成为死锁局面,如果没有其他措施,这种互相等待的死锁局面将一直延续下去。
而为了解决这个问题,TCP 为每一个连接设有一个持续计时器 (persistence timer)。
只要 TCP 连接的一方收到对方的零窗口通知,就启动该持续计时器。
若持续计时器设置的时间到期,就发送一个零窗口探测报文段(仅携带 1 字节的数据),而对方就在确认这个探测报文段时给出了现在的窗口值。
若窗口仍然是零,则收到这个报文段的一方就重新设置持续计时器。
若窗口不是零,则死锁的僵局就可以打破了。 皆大欢喜
TCP 的传输效率
前面有提到:当应用程序把数据传送到TCP的发送缓冲后,剩下的发送任务就由TCP控制了,可以用不同的机制来控制 TCP 报文段的发送时机
- 第一种机制是 TCP 维持一个变量,它等于最大报文段长度 MSS。只要缓存中存放的数据达到 MSS 字节时,就组装成一个 TCP 报文段发送出去。
- 第二种机制是由发送方的应用进程指明要求发送报文段,即 TCP 支持的推送 (push) 操作。
- 第三种机制是发送方的一个计时器期限到了,这时就把当前已有的缓存数据装入报文段(但长度不能超过 MSS)发送出去。
如何控制 TCP 发送报文段的时机仍然是一个较为复杂的问题。
糊涂窗口综合症
在了解怎么合理控制发送时机前,我们首先来了解一个概念:糊涂窗口综合症
糊涂窗口综合症:每次仅发送一个字节或很少几个字节的数据时,有效数据传输效率变得很低的现象。
发送方糊涂窗口综合症
现象:发送方 TCP 每次接收到一字节的数据后就发送。
这样,发送一个字节需要形成 41 字节长的 IP 数据报。效率很低。
解决方法:使用 Nagle 算法。
若发送应用进程把要发送的数据逐个字节地送到 TCP 的发送缓存,则发送方就把第一个数据字节先发送出去,把后面到达的数据字节都缓存起来。
当发送方收到对第一个数据字符的确认后,再把发送缓存中的所有数据组装成一个报文段发送出去,同时继续对随后到达的数据进行缓存。
只有在收到对前一个报文段的确认后才继续发送下一个报文段。
当到达的数据已达到发送窗口大小的一半或已达到报文段的最大长度时,就立即发送一个报文段。
接收方糊涂窗口综合症
这个的现象主要如下
当接收方的 TCP 缓冲区已满,接收方会向发送方发送窗口大小为 0 的报文。
若此时接收方的应用进程以交互方式每次只读取一个字节,于是接收方又发送窗口大小为一个字节的更新报文,发送方应邀发送一个字节的数据(发送的 IP 数据报是 41 字节长),于是接收窗口又满了,如此循环往复。
原因:接收方应用进程消耗数据太慢,例如:每次只读取一个字节。
解决方法:让接收方等待一段时间,使得或者接收缓存已有足够空间容纳一个最长的报文段,或者等到接收缓存已有一半空闲的空间。只要出现这两种情况之一,接收方就发出确认报文,并向发送方通知当前的窗口大小。
TCP的拥塞控制
拥塞控制的一般原理
首先:看看拥塞的现象
在某段时间,若对网络中某资源的需求超过了该资源所能提供的可用部分(信道不够用),网络的性能就要变坏。这种现象称为拥塞 (congestion)。
最坏结果:系统崩溃。
出现拥塞的原因,可能如下
- 点缓存的容量太小;
- 链路的容量不足;
- 处理机处理的速率太慢;
- 拥塞本身会进一步加剧拥塞;
总的来说便是
∑ 对资源需求 > 可用资源
那么,通过增加资源能解决拥塞吗?
不能。这是因为网络拥塞是一个非常复杂的问题。简单地采用上述做法,在许多情况下,不但不能解决拥塞问题,而且还可能使网络的性能更坏。
网络拥塞往往是由许多因素引起的。例如:
增大缓存,但未提高输出链路的容量和处理机的速度,排队等待时间将会大大增加,引起大量超时重传,解决不了网络拥塞;
提高处理机处理的速率会会将瓶颈转移到其他地方;
这时候我们就要用到拥塞控制去降低网络拥塞带来的影响
拥塞控制和流量控制
首先,我们来区别拥塞控制和流量控制
拥塞控制与流量控制的关系密切,它们之间也存在一些差别
拥塞控制:
防止过多的数据注入到网络中,使网络中的路由器或链路不致过载;
是一个全局性的过程,涉及到与降低网络传输性能有关的所有因素。
流量控制:
抑制发送端发送数据的速率,以使接收端来得及接收;
是点对点通信量的控制,是端到端的问题;
拥塞控制的一般原理
拥塞控制的前提:网络能够承受现有的网络负荷。
实践证明,拥塞控制是很难设计的,因为它是一个动态问题。
分组的丢失是网络发生拥塞的征兆而不是原因。
在许多情况下,甚至正是拥塞控制本身成为引起网络性能恶化、甚至发生死锁的原因。
开环控制和闭环控制
在面对设计网络时,我们通常采用开环控制方法和闭环控制方法,通俗来讲就是,前者就是尽量不让问题发生,后者是在问题发生时及时控制
开环控制
在设计网络时,事先考虑周全,力求工作时不发生拥塞;
思路:力争避免发生拥塞。
闭环控制
基于反馈环路的概念;
根据网络当前的运行状态采取相应控制措施;
思路:在发生拥塞后,采取措施进行控制,消除拥塞。
属于闭环控制的有以下几种措施:
(1) 监测网络系统,以便检测到拥塞在何时、何处发生。
(2) 将拥塞发生的信息传送到可采取行动的地方。
(3) 调整网络系统的运行以解决出现的问题。
那么如何检测网络拥塞嘞,主要指标有:
- 由于缺少缓存空间而被丢弃的分组的百分数;
- 平均队列长度;
- 超时重传的分组数;
- 平均分组时延;
- 分组时延的标准差,等等。
上述这些指标的上升都标志着拥塞的增长。
那么怎么去知道拥塞发生了呢
- 发送通知拥塞发生的分组;
- 在分组中保留表示拥塞状态的字段;
- 周期性地发出探测分组等。
那么这个时机该如何把握嘞
过于频繁,会使系统产生不稳定的振荡;
过于迟缓地采取行动又不具有任何实用价值。
解决拥塞的两条思路
增加网络可用资源;
减少用户对资源的需求。
TCP 的拥塞控制方法
TCP 采用基于窗口的方法进行拥塞控制。该方法属于闭环控制方法。
TCP发送方维持一个拥塞窗口 cwnd (Congestion Window)
发送端利用拥塞窗口根据网络的拥塞情况调整发送的数据量。
发送窗口大小不仅取决于接收方窗口,还取决于网络的拥塞状况,所以真正的发送窗口值为:
真正的发送窗口值 = Min (接收方窗口值,拥塞窗口值)
实现思路
只要网络没有出现拥塞,拥塞窗口就可以再增大一些,以便把更多的分组发送出去,这样就可以提高网络的利用率。
但只要网络出现拥塞或有可能出现拥塞,就必须把拥塞窗口减小一些,以减少注入到网络中的分组数,以便缓解网络出现的拥塞。
判断拥塞
重传定时器超时:网络已经发生了拥塞。
收到三个重复的 ACK:预示网络可能会出现拥塞(实际可能还未发生拥塞)。
TCP拥塞控制算法
慢开始 (slow-start)
目的:用来确定网络的负载能力或拥塞程度。
算法的思路:由小到大逐渐增大拥塞窗口数值。
两个变量:
拥塞窗口:
初始拥塞窗口值:2 种设置方法。
1 至 2 个最大报文段 (旧标准)
2 至 4 个最大报文段 (RFC 5681)窗口值逐渐增大。
慢开始门限:
防止拥塞窗口增长过大引起网络拥塞。
拥塞窗口 cwnd 控制方法:在每收到一个对新的报文段的确认后,可以把拥塞窗口增加最多一个 SMSS 的数值。
拥塞窗口 cwnd 每次的增加量 = min (N, SMSS)
其中 N 是原先未被确认的、但现在被刚收到的确认报文段所确认的字节数。
不难看出,当 N < SMSS 时,拥塞窗口每次的增加量要小于 SMSS。
用这样的方法逐步增大发送方的拥塞窗口 cwnd,可以使分组注入到网络的速率更加合理。
发送方每收到一个对新报文段的确认(重传的不算在内)就使 cwnd 加 1。
每经过一个传输轮次,拥塞窗口就加倍。
使用慢开始算法后,每经过一个传输轮次 (transmission round),拥塞窗口 cwnd 就加倍。
一个传输轮次所经历的时间其实就是往返时间 RTT。
“传输轮次”更加强调:把拥塞窗口 cwnd 所允许发送的报文段都连续发送出去,并收到了对已发送的最后一个字节的确认。
例如,拥塞窗口 cwnd = 4,这时的往返时间 RTT 就是发送方连续发送 4 个报文段,并收到这 4 个报文段的确认,总共经历的时间。
慢开始门限 ssthresh 的用法如下:
当 cwnd < ssthresh 时,使用慢开始算法。
当 cwnd > ssthresh 时,停止使用慢开始算法而改用拥塞避免算法。
当 cwnd = ssthresh 时,既可使用慢开始算法,也可使用拥塞避免算法。
拥塞避免 (congestion avoidance)
思路:让拥塞窗口 cwnd 缓慢地增大,避免出现拥塞。
每经过一个传输轮次,拥塞窗口 cwnd = cwnd + 1。
使拥塞窗口 cwnd 按线性规律缓慢增长。
在拥塞避免阶段,具有 “加法增大” (Additive Increase) 的特点。
在超时之前,每经过一个传输轮次就使 cwnd 加 1。
当网络出现拥塞时
无论在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(重传定时器超时):
ssthresh = max (cwnd/2,2)
cwnd = 1
执行慢开始算法
目的:迅速减少主机发送到网络中的分组数,使得发生拥塞的路由器有足够时间把队列中积压的分组处理完毕。
当 TCP 连接进行初始化时,将拥塞窗口置为 1。图中的窗口单位不使用字节而使用报文段。
慢开始门限的初始值设置为 16 个报文段,即 ssthresh = 16。
慢开始和拥塞避免算法的实现GIF图举例
必须强调指出
“拥塞避免”并非指完全能够避免了拥塞。利用以上的措施要完全避免网络拥塞还是不可能的。
“拥塞避免”是说在拥塞避免阶段把拥塞窗口控制为按线性规律增长,使网络比较不容易出现拥塞。
快重传 (fast retransmit)
发送方只要一连收到三个重复确认,就知道接收方确实没有收到报文段,因而应当立即进行重传(即“快重传”),这样就不会出现超时,发送方也不就会误认为出现了网络拥塞。
使用快重传可以使整个网络的吞吐量提高约20%。
不难看出,快重传并非取消重传计时器,而是在某些情况下可以更早地(更快地)重传丢失的报文段。
采用快重传 FR (Fast Retransmission) 算法可以让发送方尽早知道发生了个别报文段的丢失。
快重传 算法首先要求接收方不要等待自己发送数据时才进行捎带确认,而是要立即发送确认,即使收到了失序的报文段也要立即发出对已收到的报文段的重复确认。
快恢复 (fast recovery)
当发送端收到连续三个重复的确认时,由于发送方现在认为网络很可能没有发生拥塞,因此现在不执行慢开始算法,而是执行快恢复算法 FR (Fast Recovery) 算法:
- 慢开始门限 ssthresh = 当前拥塞窗口 cwnd / 2 ;
- 新拥塞窗口 cwnd = 慢开始门限 ssthresh ;
- 开始执行拥塞避免算法,使拥塞窗口缓慢地线性增大。
因此,在图的点4,发送方知道现在只是丢失了个别的报文段。于是不启动慢开始,而是执行快恢复算法。这时,发送方调整门限值 ssthresh = cwnd / 2 = 8,同时设置拥塞窗口 cwnd = ssthresh = 8(见图中的点5),并开始执行拥塞避免算法。
加法增大,乘法减小 (AIMD)
可以看出,在拥塞避免阶段,拥塞窗口是按照线性规律增大的。这常称为“加法增大” AI (Additive Increase)。
当出现超时或3个重复的确认时,就要把门限值设置为当前拥塞窗口值的一半,并大大减小拥塞窗口的数值。这常称为“乘法减小”MD (Multiplicative Decrease)。
二者合在一起就是所谓的 AIMD 算法。
发送方的发送窗口的上限值应当取为接收方窗口 rwnd 和拥塞窗口 cwnd 这两个变量中较小的一个,即应按以下公式确定:
发送窗口的上限值 = Min [rwnd, cwnd]
当 rwnd < cwnd 时,是接收方的接收能力限制发送窗口的最大值。
当 cwnd < rwnd 时,则是网络的拥塞限制发送窗口的最大值。
也就是说,rwnd 和 cwnd 中数值较小的一个,控制了发送方发送数据的速率。
主动队列管理 AQM
TCP 拥塞控制和网络层采取的策略有密切联系。
若路由器对某些分组的处理时间特别长,那么这就可能使这些分组中的TCP报文段经过很长时间才能到达终点,结果引起发送方超时,对这些报文段进行重传。
重传会使 TCP 连接的发送端认为在网络中发生了拥塞,但实际上网络并没有发生拥塞。
对 TCP 拥塞控制影响最大的就是路由器的分组丢弃策略。
“先进先出”FIFO 处理规则
路由器的队列通常都是按照“先进先出”FIFO (First In First Out) 的规则处理到来的分组。
当队列已满时,以后再到达的所有分组(如果能够继续排队,这些分组都将排在队列的尾部)将都被丢弃。这就叫做尾部丢弃策略 (tail-drop policy)。
路由器的尾部丢弃往往会导致一连串分组的丢失,这就使发送方出现超时重传,使 TCP 进入拥塞控制的慢开始状态,结果使 TCP 连接的发送方突然把数据的发送速率降低到很小的数值。
分组丢弃使发送方出现超时重传,使 TCP 连接进入拥塞控制的慢开始状态。如下图
全局同步
更为严重的是,在网络中通常有很多的 TCP 连接,这些连接中的报文段通常是复用在网络层的 IP 数据报中传送的。
在这种情况下,若发生了路由器中的尾部丢弃,就可能会同时影响到很多条 TCP 连接,结果使这许多 TCP 连接在同一时间突然都进入到慢开始状态。这在 TCP 的术语中称为全局同步 (global syncronization)。
全局同步使得全网的通信量突然下降了很多,而在网络恢复正常后,其通信量又突然增大很多。
分组丢弃使发送方出现超时重传,使多个 TCP 连接同时进入拥塞控制的慢开始状态,发生全局同步。如下图
主动队列管理AQM
1998 年提出了主动队列管理 AQM (Active Queue Management)。
所谓“主动”就是不要等到路由器的队列长度已经达到最大值时才不得不丢弃后面到达的分组,而是在队列长度达到某个值得警惕的数值时(即当网络拥塞有了某些拥塞征兆时),就主动丢弃到达的分组。
AQM 可以有不同实现方法,其中曾流行多年的就是随机早期检测 RED (Random Early Detection)。
使路由器的队列维持两个参数:队列长度最小门限 THmin 和最大门限 Thmax 。
RED 对每一个到达的分组都先计算平均队列长度 LAV 。
- 若平均队列长度小于最小门限 THmin,则将新到达的分组放入队列进行排队。
- 若平均队列长度超过最大门限 Thmax ,则将新到达的分组丢弃。
- 若平均队列长度在最小门限 THmin 和最大门限 Thmax 之间,则按照某一概率 p 将新到达的分组丢弃。
RED 将路由器的到达队列划分成为三个区域:
当 LAV > Thmin 时,丢弃概率 p = 0。
当 LAV > Thmax 时,丢弃概率 p = 1。
当 Thmin < LAV < Thmax时, 0 < p < 1 。
在 RED 的操作中,最难处理的就是丢弃概率 p 的选择,因为 p 并不是个常数。例如,按线性规律变化,从 0 变到 pmax。
多年的实践证明,RED 的使用效果并不太理想。
2015 年公布的 RFC 7567 已经把 RFC 2309 列为“陈旧的”,并且不再推荐使用 RED。
对路由器进行主动队列管理 AQM 仍是必要的。
AQM 实际上就是对路由器中的分组排队进行智能管理,而不是简单地把队列的尾部丢弃。
现在已经有几种不同的算法来代替旧的 RED,但都还在实验阶段。
TCP的运输连接管理
TCP 是面向连接的协议。
TCP 连接有三个阶段:
- 连接建立
- 数据传送
- 连接释放
TCP 连接的管理就是使 TCP 连接的建立和释放都能正常地进行
TCP 连接建立过程中要解决的三个问题
要使每一方能够确知对方的存在。
要允许双方协商一些参数(如最大窗口值、是否使用窗口扩大选项和时间戳选项以及服务质量等)。
能够对运输实体资源(如缓存大小、连接表中的项目等)进行分配。
TCP 连接的建立采用客户服务器方式。主动发起连接建立的应用进程叫做客户 (client)。被动等待连接建立的应用进程叫做服务器 (server)。
TCP 的连接建立(三报文握手)
TCP 建立连接的过程叫做握手。
握手需要在客户和服务器之间交换三个 TCP 报文段。称之为三报文握手。
实现过程
通过连续的动画来理解这个过程
存在这样两台机子——客户A,服务器B,它们都处于关机状态
现在B的 TCP 服务器进程先创建传输控制块TCB,准备接受客户进程的连接请求。
这时候A想要和B进行通信,它开机了,然后A 的 TCP 向 B 发出连接请求报文段,其首部中的同步位 SYN = 1,并选择序号 seq = x,表明传送数据时的第一个数据字节的序号是 x。
B 的 TCP 收到连接请求报文段后,如同意,则发回确认。
B 在确认报文段中应使 SYN = 1,使 ACK = 1,其确认号 ack = x + 1,自己选择的序号 seq = y。
A 收到此报文段后向 B 给出确认,其 ACK = 1,确认号 ack = y + 1。
A 的 TCP 通知上层应用进程,连接已经建立。
B 的 TCP 收到主机 A 的确认后,也通知其上层应用进程:TCP 连接已经建立。他们之间可以进行数据通信了
完整的TCP三报文握手示意图
为什么要采取三报文握手
这里初学会有一个疑问,为什么要采取三报文握手呢,客户为什么还要再发一次确认收到呢,除了网上大家通俗的理解:为了让服务器知道我知道了外,主要还有以下原因——为了防止已失效的连接请求报文段突然又传送到了,因而产生错误。
形象理解一下这句话,假设现在不需要第三个报文,然后发生这么一个故事
客户端在发送请求的过程中,第一个请求因为某些原因,在路上迷路了,客户端会再发送一次请求,以建立连接,等到连接释放,他想要的数据服务器已经给回它了的时候,巧了,迷路的请求来到服务器这边了,服务器就误以为客户端又要发起一次连接,于是响应过去,连接建立,但客户端此时不需要交互了啊,所以它也觉得莫名其妙,你服务器和我连接干啥,又没叫你,自作多情,然后客户端就没搭理服务器端,就这样,服务器端白白浪费了资源,一直处于连接过程
所以,为了解决客户端是真心想要和服务器建立连接的,它必须发送确认报文,用以确认连接正式建立
TCO三次握手翻译得并不准确
还有一点需要注意:网上广而流传的TCP三次握手这个说法并不准确,书中提到,three way handshake,直译过来成三报文握手更合适,因为handshake是单数,而不是复数,这意味着,它们更像是通过握手上下摇晃三次,而不是握了三次手,RFC 793 文档中也有另外的表述,three way (three message)handshake,更加印证了这个译名。
TCP四报文握手
B发送给A的报文段可以分成两个,也就是先发送一个确认报文段(ACK = 1, ack = x + 1),然后再发送一个同步报文段(SYN = 1, seq = y),这样就变成了四报文握手,但效果一样
TCP 的连接释放(四报文握手)
TCP 连接释放过程比较复杂。
数据传输结束后,通信的双方都可释放连接。
TCP 连接释放过程是四报文握手。
过程
数据传输结束后,通信的双方都可释放连接。
现在 A 的应用进程先向其 TCP 发出连接释放报文段,并停止再发送数据,主动关闭 TCP 连接。
A 把连接释放报文段首部的FIN = 1,其序号seq = u,等待 B 的确认。
B 发出确认,确认号 ack = u+1,而这个报文段自己的序号 seq = v。
TCP 服务器进程通知高层应用进程。
从 A 到 B 这个方向的连接就释放了,TCP 连接处于半关闭状态。B 若发送数据,A 仍要接收。
若 B 已经没有要向 A 发送的数据,其应用进程就通知 TCP 释放连接。
A 收到连接释放报文段后,必须发出确认。
在确认报文段中ACK = 1,确认号 ack = w + 1,自己的序号 seq = u + 1。
完整图
A 必须等待 2MSL 的时间
在这个图中你会发现:A 必须等待 2MSL 的时间,为什么呢,原因有两个
为了保证 A 发送的最后一个 ACK 报文段能够到达 B。
假设A最后一个确认连接释放报文没有被B正常收到时,B会超时重传连接释放报文,而如果这时候A急着关闭,就不知道B发过来了,所以需要等一会时间保证自己的报文成功被B收到
防止 “已失效的连接请求报文段”出现在本连接中。
同前面讲的一样,在经过2MSL时间后,A会将本连接的所有产生的报文从网络上消失掉,不管你是否丢失,都把你逮住,然后让你消失,防止下一次新连接中会出现旧连接的请求报文
保活计时器
干啥的:用来防止在TCP连接出现长时期的空闲。
工作方式是啥:保活计时器 通常设置为2小时 。若服务器过了2小时还没有收到客户的信息,它就发送探测报文段。若发送了10个探测报文段(每一个相隔75秒)还没有响应,就假定客户出了故障,因而就终止该连接。
TCP 的有限状态机
不行了不行了,整理不下去了,写不下去了,直接丢图
解释下图内标注含义⬇
箭头旁边的字,表明引起这种变迁的原因,或表明发生状态变迁后又出现什么动作。
图中有三种不同的箭头。
- 粗实线箭头表示对客户进程的正常变迁。
- 粗虚线箭头表示对服务器进程的正常变迁。
- 细线箭头表示异常变迁。