计算机网络

传输层服务与TCP、UDP协议

2018-08-14  本文已影响192人  CodeKing2017

传输层提供的服务

    传输层的功能

从通信和信息处理的角度看 ,传输层向它上面的应用层提供通信服务,它属于面向通信部分的最高层,同时也是用户功能中的最低层 。

传输层位于网络层之上 ,它为运行在不同主机上的进程之间提供了逻辑通信,而网络层提供了主机之间的逻辑通信  。显然,即使当网络层协议是不可靠的 ,也就是即使网络层协议会使分组丢失、混乱和重复 ,传输层同样也能为应用程序提供可靠的服务 。

从下图可以看出,网络的边缘部分中的两个主机使用网络的核心部分的功能进行端到端的通信时 ,只有主机的协议栈才有传输层和应用层 ,而路由器在转发分组时都只用到下三层的功能

( 即在通信子网中没有传输层 ,传输层只存在于通信子网以外的主机中)。

传输层为相互通信的进程提供逻辑通信

传输层的功能 :

1)    传输层提供应用进程之间的逻辑通信 (即端到端的通信)。与网络层的区别是 ,网络层提供的是主机之间的逻辑通信 。

从网络层来说 ,通信的双方是两个主机 ,IP 数据报的首部给出了这两个主机的 IP 地址。但 “两个主机之间的通信” 实际上是两个主机中的应用进程之间的通信 ,应用进程之间的通信又称端到端的逻辑通信 。这里“逻辑通信”  的意思是:传输层之间的通信好像是沿水平方向传送数据, 但事实上这两个传输层之间并没有一条水平方向的物理连接  。

2) 复用和分用 。复用是指发送方不同的应用进程都可以使用同一个传输层协议传送数据;分用是指接收方的传输层在剥去报文的首部后能够把这些数据正确交付到目的应用进程。

注意:传输层的复用分用功能与网络层的复用分用功能不同 。网络层的复用是指发送方不同协议的数据都可以封装成 IP 数据报发送出去 ,分用是指接收方的网络层在剥去首部后把数据交付给相应的协议。

3) 传输层还要对收到的报文进行差错检测 (首部和数据部分) 。而网络层只检查 IP 数据报的首部,不检验数据部分是否出错。

4) 提供两种不同的传输协议 ,即面向连接的 TCP 和无连接的 UDP 。而网络层无法同时实现两种协议(即在网络层要么只提供面向连接的服务,如虚电路 ,要么只提供无连接服务 ,如数据报,而不可能在网络层同时存在这两种方式) 。

传输层向高层用户屏蔽了底层网络核心的细节 ( 如网络拓扑 、路由协议等) ,它使应用进程看见的就是好像在两个传输层实体之间有一条端到端的逻辑通信信道 ,这条逻辑通信信道对上层的表现却因传输层协议不同而有很大的差别 。当传输层采用面向连接的 TCP时,尽管下面的网络是不可靠的 ,但这种逻辑通信信道就相当于是一条全双工的可靠信道。但当传输层采用无连接的UDP 时,这种逻辑通信信道仍然是一条不可靠信道 。

传输层的寻址与端口

    端口的作用

端口能够让应用层的各种应用进程将其数据通过端口向下交付给传输层 ,以及让传输层知道应当将其报文段中的数据向上通过端口交付给应用层相应的进程 。端口就是传输层服务访问点 TSAP ,它在传输层的作用类似于 IP 地址在网络层的作用或 MAC 地址在数据链路层的作用 ,只不过 IP 地址和 MAC 地址标识的是主机 ,而端口标识的是主机中的应用进程 。

数据链路层的 SAP 是 MAC 地址,网络层的 SAP 是 IP 地址,传输层的SAP是端口。 

在协议栈层间的抽象的协议端口是软件端口 ,和路由器或交换机上的硬件端口是完全不同的概念。硬件端口是不同硬件设备进行交互的接口  ,而软件端口是应用层的各种协议进程与传输实体进行层间交互的一种地址 。传输层使用的是软件端口 。

端口号只具备本地意义 ,即端口号只是为了标志本计算机应用层中的各进程  。在因特网中不同计算机的相同端口号是没有联系的 。

    端口号

应用进程通过端口号进行标识 ,端口号长度为 16bit,能够表示 65536 (2^16)个不同的端口号。端口号只具有本地意义,即端口号只是为标志本计算机应用层中的各进程 ,不同计算机的相同端口号是没有联系的  。根据端口号范围可将端口分为两类:

1) 服务端使用的端口号 。这里又分为两类 ,最重要的一类是熟知端口号 ,数值为 0 ~1023, IANA ( 互联网地址指派机构) 把这些端口号指派给了 TCP/IP 最重要的一些应用程序 ,让所有的用户都知道 。另一类叫做登记端口号 ,数值为 1024~ 49151 。它是为没有熟知端口号的应用程序使用的,使用这类端口号必须在IANA登记,以防止重复。

一些常用的熟知端口号如下 :

2) 客户端使用的端口号 ,数值为 49152 ~ 65535 ,由于这类端口号仅在客户进程运行时才动态选择 ,因此又叫短暂端口号( 也称临时端口)。通信结束后 ,刚才使用过的客户端口号就不复存在,这个端口号就可以供其他客户进程使用 。

    套接字

在网络中通过 IP 地址来标识和区别不同的主机 ,通过端口号来标识和区分一台主机中的不同应用进程。在网络中采用发送方和接收方的套接宇 ( Socket ) 组合来识别端点 。所谓套接字实际上是一个通信端点 ,即,

套接字=( 主机 IP 地址 ,端口号) 它唯一地标识了网络中的一个主机和其上的一个应用(进程)。

在网络通信中 ,主机 A 发给主机 B 的报文段包含目的端口号和源端口号 ,源端口号是作为 “ 返回地址” 的一部分,即当B需要发回给一个报文段给 A 时,B 到 A 的报文段中目的端口号便是 A 到 B 报文段中源端口号 (完全的返回地址是 A 的 IP 地址和源端口号) 。

无连接服务与面向连接服务

面向连接的服务就是在通信双方进行通信之前  ,必须先建立连接 ,在通信过程中 ,整个连接的情况一直被实时地监控和管理 。当通信结束后 ,则应该释放这个连接 。

而无连接的服务,两个实体之间的通信不需要先建立好连接,需要通信的时候,直接将信息发送到  “ 网络”  中,让该信息的传递在网上尽力而为地往目的地传送。

TCP/IP 协议族在 IP 层之上使用了两个传输协议 :一个是面向连接的传输控制协议 TCP ,当采用 TCP 时,传输层向上提供的是一条全双工的可靠逻辑信道;另一个是无连接的用户数据报协议 UDP,当采用UDP 时,传输层向上提供的是一条不可靠的逻辑信道 。

TCP 提供面向连接的服务 ,在传送数据之前必须先建立连接,数据传送结束后要释放连接 。 TCP 不提供广播或组播服务 。由于 TCP 提供面向连接的可靠的传输服务 ,因此不可避免地增加了许多开销,如确认、流量控制 、计时器以及连接管理等 。这不仅使协议数据单元的头部增大很多,还要占用许多的处理机资源。因此 TCP 主要适用于可靠性更重要的场合 ,如文件传输协议 FTP 、超文本传输协议 HTTP、远程登录 TELNET 等。

UDP 是一个无连接的非可靠的传输层协议 。它在 IP 之上仅提供两个附加服务 :多路复用和对数据的错误检查 。IP 知道怎样把分组投递给一台主机 ,但不知道怎样把它们投递给主机上的具体的应用 。UDP 在传送数据之前不需要先建立连接 ,远程主机的传输层收到 UDP 报文后 ,不需要给出任何确认 。由于UDP 比较简单,其执行速度就比较快 、实时性好 。使用 UDP 的应用主要包括小文件传送协议 ( TFTP )、DNS 、SNMP 和实时协议( RTP )。

注意:  IP 数据报和 UDP 数据报的区别 :IP 数据报在网络层要经过路由的存储转发 ;而 UDP 数据报是在传输层的端到端的逻辑信道中传输 ,而封装成 IP 数据报在网络层传输时 ,UDP 数据报的信息对路由是不可见的 。

UDP 协议

UDP 概述

[RFC 768]定义的 UDP 只是做了传输协议能够做的最少工作 ,只在 IP 的数据报服务之上增加了两个最基本的服务 :复用和分用以及差错检测。如果应用程序开发者选择UDP 而不是 TCP,则应用程序几乎是直接与 IP 打交道。

也许你想知道 ,为什么应用开发人员宁愿在 UDP 之上构建应用 ,也不选择 TCP?既然 TCP 提供可靠的服务 ,而 UDP 不提供,那么TCP 总是首选吗?答案是否定的 ,因为有很多应用更适合用 UDP ,主要是因为 UDP 有下列的优点 :

1)   UDP 无需建立连接 。因此 UDP 不会引入建立连接的时延 。试想如果 DNS 运行在 TCP 之上而不是 UDP ,则 DNS 的速度会慢很多 。HTTP 使用 TCP 而不是 UDP ,是因为对于基于文本数据的 Web 网页来说,可靠性是至关重要的 。

2)无连接状态 。TCP 需要在端系统中维护连接状态 。此连接状态包括接收和发送缓存 、拥塞控制参数和序号与确认号的参数 。而 UDP 不维护连接状态 ,也不跟踪这些参数 。因此,某些专用应用服务器使用 UDP 时,一般都能支持更多的活动客户机 。

3)分组首部开销小 。TCP 有 20 字节的首部开销 ,而 UDP 仅有 8 字节的开销。

4)应用层能更好地控制要发送的数据和发送时间 。UDP 没有拥塞控制 ,因此网络中的拥塞也不会影响主机的发送效率 。某些实时应用要求以稳定的速度发送 ,能容忍一些数据的丢失 ,但不允许有较大的时延 ,而 UDP 正好满足这些应用的需求 。

UDP 常用于一次性传输比较少量数据的网络应用 ,如 DNS、SNMP 等,因为对于这些应用,若采用 TCP ,则将为连接创建 、维护和拆除而带来不小的开销 。UDP 也常用于多媒体应用 ( 如 IP 电话、实时视频会议 、流媒体等),显然,可靠数据传输对这些应用来说并不是最重要的 ,但 TCP 的拥塞控制会导致数据出现较大的延迟 ,这是它们不可容忍的 。

UDP 提供尽最大努力的交付 ,即不保证可靠交付,但这并不意味着应用对数据的要求是不可靠的,因此所有维护传输可靠性的工作需要用户在应用层来完成。应用实体可以根据应用的需求来灵活设计自己的可靠性机制 。

UDP 是面向报文的 。发送方 UDP 对应用层交下来的报文 ,在添加首部后就向下交付给 IP 层 ,既不合并,也不拆分 ,而是保留这些报文的边界;接收方 UDP 对 IP 层交上来 UDP 用户数据报 ,在去除首部后就原封不动地交付给上层应用进程 ,一次交付一个完整的报文 。因此报文不可分割 ,是UDP 数据报处理的最小单位 。

UDP 的首部格式

UDP 数据报包含两个部分 :UDP 首部和用户数据 ,整个 UDP 数据报作为 IP 数据报的数据部分封装在 IP 数据报中 ,如图所示。UDP 首部有 8 个字节,由4 个字段组成 ,每个字段的长度都是两个字节 ,各字段意义如下 :

1)   源端口  源端口号。在需要对方回信时选用 。不需要时可用全 0。

2) 目的端口  目的端口号。这在终点交付报文时必须要使用到 。

3) 长度  UDP 数据报的长度 (包括首部和数据) ,其最小值是 8(仅有首部)。

4) 校验和  检测 UDP 数据报在传输中是否有错 。有错就丢弃。该字段是可选的,当源主机不想计算校验和 ,则直接令该字段为全 0。

当传输层从 IP 层收到 UDP 数据报时 ,就根据首部中的目的端口,把 UDP 数据报通过相应的端口,上交给应用进程,如图所示。

如果接收方 UDP 发现收到的报文中的目的端口号不正确 (即不存在对应于端口号的应用进程),就丢弃该报文,并由 ICMP发送 “端口不可达” 差错报文给发送方 。

UDP 校验

在计算校验和时 ,要在UDP 数据报之前增加 12 个字节的伪首部 ,伪首部并不是 UDP 真正的首部。只是在计算校验和时 ,临时添加在 UDP 数据报的前面 ,得到一个临时的 UDP 数据报。 校验和就是按照这个临时的 UDP 数据报计算的 。伪首部既不向下传送也不向上递交,而仅仅是为了计算校验和 。这样的校验和 ,既检查了 UDP 数据报,又对 IP 数据报的源 IP 地址和目的 IP 地址进行了检验 。下图给出了 UDP 数据报的伪首部各宇段的内容 。

UDP 校验和的计算方法和 IP 数据报首部校验和的计算方法相似 ,都使用二进制反码运算求和再取反 。但不同的是 :IP数据报的校验和只检验 IP 数据报的首部 ,但UDP 的校验和是把首部和数据部分一起都检验 。

在发送方 ,首先是把全零放入校验和字段并且添加伪首部 。然后,把 UDP  数据报看成是由许多 16 位的字串连接起来 。若 UDP 数据报的数据部分不是偶数个字节 ,则要在数据部分末尾增加一个全零字节 (但此字节不发送)。接下来就按二进制反码计算出这些16 位字的和。将此和的二进制反码写入校验和字段 。在接收方 ,把收到的 UDP 数据报加上伪首部 (如果不为偶数个字节,还需要补上全零字节)后,按二进制反码计算出这些 16 位字的和 。当无差错时其结果应全为 1。否则就表明有差错出现 ,接收方就应该丢弃这个 UDP 数据报。

下图给出了一个计算 UDP 校验和的例子 。本例中,UDP 数据报的长度是 15 字节 (不含伪首部),因此需要添加一个全 0 字节。

注意:1) 校验时 ,若 UDP 数据报部分的长度不是偶数个字节 ,则需要填入一个全 0 字节 ,如上图所示。但是此字节和伪首部一样,是不发送的。

2) 如果 UDP 校验和校验出 UDP 数据报是错误的 ,可以丢弃 ,也可以交付给上层 ,但是需要附上错误报告 ,即告诉上层这是错误的数据报 。

3) 通过伪首部 ,不仅可以检查源端口号 、目的端口号和 UDP 用户数据报的数据部分 ,还可以检查 IP 数据报的源 IP 地址和目的地址。

这种简单的差错检验方法的检错能力并不强  ,但它的好处是简单 ,处理速度快。

TCP 协议

TCP 协议的特点

TCP  是在不可靠的 IP 层之上实现的可靠的数据传输协议 ,它主要解决传输的可靠 、有序、 无丢失和不重复的问题 。TCP 是 TCP/IP 体系中非常复杂的一个协议 ,主要特点有 :

1)  TCP 是面向连接的传输层协议 。

2)每一条 TCP 连接只能有两个端点 ,每一条 TCP 连接只能是点对点的 (一对一)。

3)  TCP 提供可靠的交付服务 ,保证传送的数据无差错 、不丢失 、不重复且有序 。

4)TCP 提供全双工通信 ,TCP 允许通信双方的应用进程在任何时候都能发送数据 ,为此 TCP连接的两端都设有发送缓存和接收缓存 ,用来临时存放双向通信的数据 。 发送缓存用来暂时存放以下数据 :①发送应用程序传送给发送方TCP准备发送的数据 ;②TCP 已发送但尚未收到确认的数据 。接收缓存用来暂时存放以下数据 :①按序到达的但尚未被接收应用程序读取的数据 ;②不按序到达的数据 。

5)  TCP 是面向字节流的 ,虽然应用程序和 TCP 的交互是一次一个数据块( 大小不等),但TCP 把应用程序交下来的数据看成仅仅是一连串的无结构的字节流 。

TCP 报文段

TCP 传送的数据单元称为报文段 。一个 TCP 报文段分为 TCP 首部和 TCP 数据两部分 ,整个 TCP 段作为 IP 数据报的数据部分封装在 IP 数据报中 ,如图所示。其首部的前 20 字节是固定的。TCP 报文段的首部最短为 20 字节 ,后面有 4N 字节是根据需要而增加的选项 ,通常长度为 4 字节的整数倍 。

TCP 报文段既可以用来运载数据 ,也可以用来建立连接 、释放连接和应答 。 各字段意义如下 :

1)   源端口和目的端口字段 各占 2 字节。端口是运输层与应用层的服务接口 。运输层的复用和分用功能都要通过端口才能实现 。

2) 序号字段 占 4 字节。TCP 是面向字节流的 (就是说 TCP 传送时是按照一个一个字节来传送的),所以TCP 连接中传送的数据流中的每一个字节都编上一个序号 。序号字段的值则指的是本报文段所发送的数据的第一个字节的序号 。

例如 ,一报文段的序号字段值是 301,而携带的数据共有 100 字

节,这就表明本报文段的数据的最后一个字节的序号是 400 ,故下一个报文段的数据序号应从 401 开始。

3) 确认号字段 占 4 字节,是期望收到对方的下一个报文段的数据的第一个字节的序号  。 若确认号 N ,则表明到序号 N-1  为止的所有数据都已正确收到 。

例如,B 正确收到了 A 发送过来的一个报文段 ,其序号字段是指501 ,而数据长度是200 字节 (序号 501 ~ 700 ) ,这表明 B 正确收到了 A 发送的到序号 700 为止的数据。因此 B 期望收到 A 的下一个数据序号是 701,于是 B 在发送给 A  的确认报文段中把确认号置为 701 。

4) 数据偏移(即首部长度) 占 4 位,这里不是 IP 数据报分片的那个数据偏移 ,而是表示首部长度 ,它指出 TCP 报文段的数据起始处距离 TCP 报文段的起始处有多远 。

“数据偏移” 的单位是 2 位 ( 以4字节为计算单位) 。因此当此字段的值为 15 时,达到TCP首都的最大长度 60 字节。

5) 保留字段 占 6 位,保留为今后使用 ,但目前应置为 0,该字段可以忽略不计 。

6) 紧急位 URG  当 URG=1 时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据 ,应尽快传送 (相当于高优先级的数据)。但是 URG  需要和紧急指针配套使用 ,也就是说数据从第一个字节到紧急指针所指字节就是紧急数据  。

7) 确认位 ACK  只有当 ACK=1  时确认号字段才有效 。当 ACK=0 时,确认号无效 。

TCP 规定,在连接建立后所有传送的报文段都必须把 ACK 置 1 。

8) 推送位 PSH  (Push)  接收 TCP 收到 PSH=1 的报文段,就尽快地交付接收应用进程 ,而不再等到整个缓存都填满了后再向上交付 。

9) 复位位 RST  ( Reset )    当 RST=1 时,表明TCP 连接中出现严重差错 (如由于主机崩溃或其他原因),必须释放连接,然后再重新建立运输连接  。

10) 同步位 SYN   同步 SYN=1 表示这是一个连接请求或连接接收报文 。

当 SYN=1,ACK=0 时,表明这是一个连接请求报文 ,对方若同意建立连接 ,则在响应报文中使用 SYN=1 ,  ACK=1 。即,SYN=1 就表示这是一个连接请求或连接接收报文 。

11)   终止位 FIN(Finish )    用来释放一个连接 。FIN=1 表明此报文段的发送方的数据己发送完毕 ,并要求释放传输连接 。

12) 窗口字段   占 2 字节。它指出了现在允许对方发送的数据量,接收方的数据缓存空间是有限的,故用窗口值作为接收方让发送方设置其发送窗口的依据 ,单位为字节 。

例如,设确认号是 701 ,窗口字段是 1000。这就表明 ,从 701 号算起,发送此报文段的一方还有接收 1000 字节数据 (字节序号是 701 ~ 1700) 的接收缓存空间。

13) 检验和  占 2 字节。检验和字段检验的范围包括首部和数据这两部分。在计算检验和时 , 和 UDP 一样,要在TCP 报文段的前面加上 12 字节的伪首部(只需将 UDP 伪首部的第 4 个字段, 即协议字段的 17 改成 6,其他的和 UDP  一样)。

14) 紧急指针字段  占 16 位,指出在本报文段中紧急数据共有多少个字节 (紧急数据放在本报文段数据的最前面) 。

15) 选项字段  长度可变 。TCP  最初只规定了一种选项 ,即最大报文段长度 ( MaximumSegment Size, MSS ) 。MSS 是 TCP 报文段中的数据字段的最大长度 。

16) 填充字段  这是为了使整个首部长度是 4 字节的整数倍 。

TCP 连接管理

TCP 是面向连接的协议 。因此每一个 TCP 连接都有三个阶段 :连接建立、数据传送和连接释放。TCP  连接的管理就是使运输连接的建立和释放都能正常进行 。

在 TCP 连接建立的过程中要解决以下三个问题 :

1)   要使每一方都能够确知对方的存在 。

2) 要允许双方协商一些参数 ( 如最大窗口值 、是否使用窗口扩大选项 、时间戳选项以及服务质量等)。

3) 能够对运输实体资源 (如缓存大小、连接表中的项目等)进行分配。

TCP 把连接作为最基本的抽象 ,每一条 TCP 连接有两个端点 ,TCP 连接的端点不是主机 , 不是主机的 IP 地址 ,不是应用进程 ,也不是传输层的协议端口 。TCP 连接的端口叫做套接字(socket)或插口 。端口拼接到 IP 地址即构成了套接字 。

每一条 TCP 连接唯一地被通信两端的两个端点 (即两个套接字) 所确定。

TCP 连接的建立采用客户/服务器方式 。主动发起连接建立的应用进程叫做客户机 (Client ),而被动等待连接建立的应用进程叫做服务器 ( Server )。

 TCP 连接的建立

连接的建立经历以下 3 个步骤,通常称为 “三次握手”,如图所示

“三次握手” 建立 TCP 连接

第一步 :客户机的 TCP 首先向服务器的 TCP 发送一个连接请求报文段 。这个特殊的报文段中不含应用层数据 ,其首部中的SYN 标志位被置为 1。另外,客户机会随机选择一个起始序号 seq=x ( 连接请求报文不携带数据 ,但要消耗掉一个序号) 。

第二步 :服务器的 TCP 收到连接请求报文段后 ,如同意建立连接 ,就向客户机发回确认 , 并为该 TCP 连接分配 TCP 缓存和变量 。在确认报文段中 ,SYN 和 ACK 位都被置为 1,确认号字段的值为 x + 1,并且服务器随机产生起始序号 seq=y ( 确认报文不携带数据 ,但也要消耗掉一个序号)。确认报文段同样不包含应用层数据 。

第三步 :当客户机收到确认报文段后 ,还要向服务器给出确认 ,并且也要给该连接分配缓存和变量 。这个报文段的 ACK  标志位被置1,序号字段为 x+1 ,确认号字段 ack=y+1 。该报文段可以携带数据,如果不携带数据则不消耗序号 。

在成功进行了以上三步之后 ,TCP 连接就建立了 ,接下来就可以传送应用层数据了 。TCP 提供的是全双工通信 ,因此通信双方的应用进程在任何时候都能发送数据 。

另外值得注意的是 ,服务器端的资源是在完成第二次握手时分配的,而客户端的资源是在完成第三次握手时分配的 。这就使得服务器易于受到 SYN 洪泛攻击 。

TCP 连接的释放

天下没有不散的宴席 ,对于 TCP 也是如此 。参与 TCP 连接的两个进程中的任何一个都能终止该连接 。TCP 连接释放的过程通常称为 “四次挥手”。如图所示。

“四次挥手” 释放 TCP 连接

第一步:客户机打算关闭连接,就向其 TCP 发送一个连接释放报文段 ,并停止再发送数据, 主动关闭 TCP 连接,该报文段的FIN 标志位被置 1, seq=u,它等于前面己传送过的数据的最后一个字节的序号加 1  ( FIN 报文段即使不携带数据 ,也要消耗掉一个序号)。TCP  是全双工的 , 即可以想象成是一条 TCP 连接上有两条数据通路 。当发送 FIN 报文时 ,发送 FIN 的一端就不能再发送数据,也就是关闭了其中一条数据通路 ,但对方还可以发送数据 。

第二步:服务器收到连接释放报文段后即发出确认,确认号是ack=u+1,而这个报文段自己的序号是 v,等于它前面已传送过的数据的最后一个字节的序号加 1。此时,从客户机到服务器这个方向的连接就释放了 ,TCP 连接处于半关闭状态 。但服务器若发送数据 ,客户机仍要接收 ,即从服务器到客户机这个方向的连接并未关闭 。

第三步:若服务器己经没有要向客户机发送的数据 ,就通知 TCP 释放连接 ,此时其发出 FIN=1的连接释放报文段 。

第四步 :客户机收到连接释放报文段后 ,必须发出确认 。在确认报文段中 ,ACK 字段被置为 1,确认号 ack=w+1,序号 seq=u+1 。此时 TCP 连接还没有释放掉 ,必须经过时间等待计时器设置的时间 2MSL 后,A 才进入到连接关闭状态 。

对上述 TCP 连接建立和释放的总结如下:

TCP 可靠传输

TCP  的任务是在 IP 层的不可靠的、尽力而为服务的基础上建立一种可靠数据传输服务 。TCP 提供的可靠数据传输服务就是要保证接收方进程从缓存区读出的字节流与发送方发出的字节流是完全一样的 。TCP 使用了校验 、序号 、确认和重传等机制来达到这个目的。其中,TCP 的校验机制与 UDP 校验一样 ,这里不再赘述。

    序号

TCP  首部的序号字段用来保证数据能有序提交给应用层 ,TCP 把数据看成一个无结构但是有序的宇节流 ,而序号是建立在传送的字节流之上 ,而不是建立在报文段之上 。

TCP  连接中传送的数据流中的每一个字节都编上一个序号 。序号字段的值则指的是本报文段所发送的数据的第一个字节的序号 。如图所示,假设 A 和 B 之间建立了一条 TCP 连接, A 的发送缓存区中总共有 10 个字节,序号从 0 开始标号 ,第一个报文包含第 0 ~  2 个字节 ,则该 TCP 报文段的序号是 0;第二个报文段的序号是 3。

    确认

TCP 首部的确认号是期望收到对方的下一个报文段的数据的第一个字节的序号 。上图中, 如果接收方 B 已收到第一个报文段 ,此时 B 希望收到的下一个报文段的数据是从第 3 个字节开始的,于是B 发送给 A 的报文中的确认号字段应该为 3。发送方缓存区会继续存储那些已经发送但未收到确认的报文段 ,以便在需要的时候重传 。

TCP  默认使用累计确认,即TCP 只确认数据流中至第一个丢失字节为止的字节 。例如,上图中,接收方 B 收到了 A 发送的包含字节 0 ~ 2 以及字节 6 ~ 7 的报文段。由于某种原因,B 还没有收到字节 3 ~ 5 的报文段,此时 B 仍在等待字节 3 (和其后面的字节),因此,B 到 A 的下一个报文段将确认号字段置为 3。

    重传

有两种事件会导致 TCP 对报文段进行重传 :超时和冗余 ACK。

(1) 超时

TCP 每发送一个报文段 ,就对这个报文段设置一次计时器 。只要计时器设置的重传时间到期但还没有收到确认 ,就要重传这一报文段 。

由于 TCP  的下层是一个互联网环境 ,IP 数据报所选择的路由变化很大 。因而传输层的往返时延的方差也很大 。为了计算超时计时器的重传时间,TCP  采用一种自适应算法 ,它记录一个报文段发出的时间 ,以及收到相应确认的时间 ,这两个时间之差叫做报文段的往返时间 ( Round-Trip Time,  RTT) 。TCP 保留了 RTT 的一个加权平均往返时间 RTTs ,当第一次测量RTT 样本时 ,RTTs 值就为所测量到的 RTT 样本的值,但以后每测量一个新的RTT样本,就按下式重新计算一次RTTs:

在上式中 0<= α<1 。若α很接近于零 ,表示新的 RTTs 值和旧的 RTTs 值相比变化不大 ,而受新的 RTT 样本影响不大 (RTT 值更新较慢)。若选择α接近于1,则表示新的RTTs 值受新的 RTT 样本的影响较大 (RTT 值更新较快)。[RFC 2988]推荐的α值为 0.125。

显然,超时计时器设置的超时重传时间 ( Retransmission Time-Out, RTO ) 应略大于上面得出的加权平均往返时间 RTTs 。使用下式计算 :

                                                                    RTO=RTTs+4xRTTD

式中,RTTD 是 RTT的偏差的加权平均值 ,它与RTTs 和新的 RTT 样本之差有关 。当第一次测量时 ,RTTD 取为测量到的 RTT 样本值的一半 ,以后测量中,使用下式计算 :

(2) 冗余 ACK (冗余确认)

超时触发重传存在的一个问题就是超时周期往往太长 。幸运的是,发送方通常可在超时事件发生之前通过注意所谓冗余 ACK 来较好地检测丢包情况 。冗余 ACK 就是再次确认某个报文段的ACK,而发送方先前已经收到过该报文段的确认。例如,发送方A 发送了序号为 1、2、3、4、5 的 TCP 报文段 ,其中 2 号报文段在链路中丢失 ,它将无法到达接收方 B。因此3、4、5 号报文段对于 B 来说就成了失序报文段 。TCP 规定每当比期望序号大的失序报文段到达时 ,发送一个冗余 ACK ,指明下一个期待字节的序号[ RFC 1122,RFC 2581] 。在本例中 ,3、4、5 号报文到达B,但它们不是 B 所期望收到的下一个报文 ,于是 B 就发送 3 个对 1 号报文段的冗余 ACK ,表 示自己期望接收 2 号报文段 。TCP 规定当发送方收到对同一个报文段的 3 个冗余 ACK 时,就可以认为跟在这个被确认报文段之后的报文段已经丢失 。就前面的例子而言 ,当 A 收到对于 1 号报文段的 3 个冗余 ACK 时,则它可以认为 2 号报文段已经丢失 。这时发送方 A 可以立即对 2 号报文段执行重传 ,这种技术通常称为快速重传 。当然,冗余 ACK 还被用在拥塞控制中 ,这将在后面的内容中讨论。

TCP 流量控制

TCP  提供了流量控制服务以消除发送方使接收方缓存区溢出的可能性  ,因此可以说流量控制是一个速度匹配服务 (匹配发送方的发送速率与接收方的读取速率) 。

TCP  提供一种基于滑动窗口协议的流量控制机制 ,滑动窗口的基本原理己在数据链路层介绍过了 ,这里要介绍的是 TCP  是如何使用窗口机制来实现流量控制的 。

在通信过程中 ,接收方根据自己接收缓存的大小 ,动态地调整发送方的发送窗口大小 ,这就是接收窗口rwnd,即调整 TCP  报文段首部中的 “窗口” 字段值,来限制发送方向网络注入报文的速率。同时,发送方根据其对当前网络拥塞程序的估计而确定的窗口值 ,称为拥塞窗口 cwnd(后面会讲到),其大小与网络的带宽和时延密切相关。

例如,在通信中 ,有效数据只从 A 发往 B,而 B 仅向 A 发送确认报文 ,这时,B 就可以通过设置确认报文段首部的窗口字段来将 rwnd 通知给 A。rwnd即接收方允许连续接收的最大能力 , 单位是字节 。发送方 A 总是根据最新收到的 rwnd 值来限制自己发送窗口的大小 ,这样可以将未确认的数据量控制在rwnd大小之内 ,保证了 A 不会使 B 的接收缓存溢出 。当然,A 的发送窗口的实际大小是取 rwnd 和 cwnd 中的最小值。

下图的例子说明了如何利用滑动窗口机制进行流量控制 。设主机 A 向主机B 发送数据, 在连接建立时 ,B 告诉 A:“我的接收窗口rwnd=400( 字节)”。

传输层和数据链路层的流量控制的区别在于:传输层定义了端到端用户之间的流量控制 ,数据链路层定义了两个中间的相邻结点的流量控制 。另外,数据链路层的滑动窗口协议的窗口大小不能动态变化 ,传输层的可以动态变化 。

TCP 拥塞控制

所谓拥塞控制就是防止过多的数据注入网络中 ,这样可以使网络中的路由器或链路不致过载。当出现拥塞时 ,端点并不能了解到拥塞发生的细节 ,对通信连接的端点来说 ,拥塞往往表现为通信时延的增加 。当然拥塞控制和流量控制也有相似的地方,都是通过控制发送方发送数据的速率来达到效果的 。

拥塞控制与流量控制的区别 :拥塞控制是让网络能够承受现有的网络负荷 ,它是一个全局性的过程,涉及所有的主机 、所有的路由器 ,以及与降低网络传输性能有关的所有因素 。相反,流量控制往往是指点对点的通信量的控制 ,即接收端控制发送端,它所要做的就是抑制发送端发送数据的速率 ,以便使接收端来得及接收

例如 ,某个链路传输速率为 10Gb/s ,某巨型机向一个 PC 以 1Gb/s 的速率传送文件 ,显然网络的带宽是足够大的 ,即不存在拥塞问题 ,但如此高的发送速率将导致 PC 可能来不及接收 ,因此流量控制是必需的 。但若有 100 万个 PC 在此链路上以 1Mb/s 的速率传送文件 ,则现在的问题就变成了网络的负载是否超过了现有网络所能承受的范围。

为了更好地对传输层进行拥塞控制 ,因特网建议标准定义了以下四种算法:慢开始、拥塞避免、快重传、快恢复。

发送方在确定发送报文段的速率时 ,既要根据接收方的接收能力,又要从全局考虑不要使网络发生拥塞 。因此,TCP 协议要求发送方维护以下两个窗口 :

1) 接收窗口 rwnd ,接收方根据目前接收缓存大小所许诺的最新的窗口值 ,反映了接收方的容量。由接收方根据其放在 TCP 报文的首部的窗口字段通知发送方 。

2)  拥塞窗口 cwnd ,发送方根据自己估算的网络拥塞程度而设置的窗口值 ,反映了网络的当前容量 。只要网络没有出现拥塞  ,拥塞窗口就再增大一些,以便把更多的分组发送出去。但只要网络出现拥辜,拥塞窗口就减小一些 ,以减少注入网络中的分组数 。

发送窗口的上限值应当取接收窗口 rwnd 和拥塞窗口 cwnd 中较小的一个,即:

                                    发送窗口的上限值 = Min[rwnd,  cwnd]

注意:接收方总是有足够大的缓存空间 ,因而发送窗口大小由网络的拥塞程度来决定 ,也就是说可以将发送窗口等同于为拥塞窗口 。

接收窗口的大小可以根据 TCP  报文首部的窗口字段通知发送方 ,而发送方怎么去维护拥塞窗口呢?这就是下面讲解的慢开始和拥塞避免算法 。

    慢开始和拥塞避免

(1) 慢开始算法

在 TCP 刚刚连接好,开始发送TCP 报文段时 ,先令拥塞窗口 cwnd=1 ,即一个最大报文段长度 MSS 。而在每收到一个对新的报文段的确认后 ,将 cwnd 加 1,即增大一个 MSS 。用这样的方法逐步增大发远方的拥塞窗口 cwnd ,可以便分组注入到网络的速率更加合理 。

例如,A 向B 发送数据 ,当发送时 A 的拥塞窗口为 2,那么 A 一次可以发送两个 TCP 报文段 ,当经过一个 RTT 后 (也称为一个传输轮次) ,A 收到 B 对刚才两个报文的确认 ,于是就把拥塞窗口调整为 4,下一次发送时就可以一次发送 4 个报文段。

使用慢开始算法后 ,每经过一个传输轮次 ( 即往返时延 RTT) ,拥塞窗口 cwnd  就会加倍, 即 cwnd  的大小呈指数形式增长 。这样慢开始一直把拥塞窗口 cwnd  增大到一个规定的慢开始门限 ssthresh (阙值),然后改用拥塞避免算法 。

(2) 拥塞避免算法

拥塞避免算法的做法是 :发送端的拥塞窗口 cwnd 每经过一个往返时延 RTT 就增加一个 MSS 的大小,而不是加倍 ,使 cwnd 按线性规律缓慢增长 (即加法增大),而当出现一次超时 ( 网络拥塞) 时,则令慢开始门限 ssthresh 等于当前 cwnd  的一半 (即乘法减小)。

根据 cwnd 的大小执行不同的算法 ,可归纳如下 :

•当 cwnd<ssthresh时,使用慢开始算法。

•当 cwnd>ssthresh 时,停止使用慢开始算法而改用拥塞避免算法 。

•当 cwnd=ssthresh 时,既可使用慢开始算法 ,也可使用拥塞避免算法(通常做法)。

(3)网络拥塞的处理

当网络出现拥塞时  ,无论在慢开始阶段还是在拥塞避免阶段,只要发送方检测到超时事件的发生 (没有按时收到确认 ,重传计时器超时) ,就要把慢开始门限 ssthresh  设置为出现拥塞时的发远方 cwnd 值的一半 (但不能小于 2 )。然后把拥塞窗口 cwnd 重新设置为 1,执行慢开始算法 。 这样做的目的就是要迅速减少主机发送到网络中 的分组数,使得发生拥靠的路由器有足够时间把队列中积压的分组处理完毕 。

拥塞避免并非完全能避免拥塞  。利用以上措施要完全避免网络拥塞是不可能的 。拥塞避免是指在拥塞避免阶段把拥塞窗口控制为接线性规律增长,使网络比较不容易出现拥塞 。

慢开始和拥塞避免算法的实现过程如图所示。

慢开始和拥塞避兔算法的实现举例

•初始时 ,拥塞窗口置为1,即cwnd=1 ,慢开始门限置为16,即ssthresh=16 。慢开始阶段, cwnd 初值为 1,以后发送方每收到一个确认 ACK, cwnd 值加 1,也即经过每个传输轮次 (RTT) , cwnd 呈指数规律增长 。

•当拥塞窗口 cwnd 增长到慢开始门限 ssthresh 时 (即当 cwnd=16 时),就改用拥塞避免算法,cwnd 按线性规律加性增长 。

•假定 cwnd=24 时,网络发生拥塞,更新 ssthresh 值为 12 ( 即变为超时时 cwnd 值 24的一 半),cwnd 重置 1,并执行慢开始算法 ,当 cwnd=12 时,改为拥塞避免算法 。

注意,在慢开始 (指数级增长) 阶段,若 2*cwnd>ssthresh ,则下一个RTT 的 cwnd 应等于 ssthresh ,而不是 2*cwnd ,即 cwnd 不能跃过 ssthresh 值。上图中,在第 16 个轮次时 ,cwnd=8、 ssthresh=12 ,第 17 个轮次时 ,cwnd=12 ,而不是 16。

在慢开始和拥塞避免算法中使用了 “乘法减小” 和 “加法增大” 方法。“乘法减小”是指不论在慢开始阶段还是拥塞避免阶段 ,只要出现一次超时 (即很可能出现了网络拥塞) ,就把慢开始门限值 ssthresh 设置为当前的拥塞窗口值的一半 。当网络频繁出现拥塞时 ,ssthresh 值就下降得很快,以大大减少注入到网络中的分组数 。而“加法增大” 是指执行拥塞避免算法后 ,在收到对所有报文段的确认后 ( 即经过一个 RTT ) ,就把拥事窗口 cwnd 增加一个 MSS 大小,使拥塞窗口缓慢增大,以防止网络过早出现拥塞  。

    快重传和快恢复 

快重传和快恢复算法是对慢开始和拥塞避免算法的改进 。

(1) 快重传

在上一节 TCP 可靠传输机制中 ,快速重传技术使用了用冗余 ACK  来检测丢包的发生 。同样, 冗余 ACK 也用于网络拥塞的检测 ( 丢了包当然意味着网络可能出现了拥塞) 。快重传并非取消重传计时器 ,而是在某些情况下可更早地重传去失的报文段  。

当发送方连续收到三个重复的 ACK  报文时 ,直接重传对方尚未收到的报文段  ,而不必等待那个报文段设置的重传计时器超时 。

(2) 快恢复

快恢复算法原理:当发送端收到连续三个冗余 ACK ( 即重复确认) 时,就执行 “乘法减小” 算法 ,把慢开始门限 ssthresh  设置为出现拥塞时发送方 cwnd  的一半。与慢开始 (慢开始算法将拥塞窗口 cwnd 设置为 1) 不同之处是它把 cwnd 的值设置为慢开始门限 ssthresh 改变后的数值 , 然后开始执行拥塞避免算法 (“加法增大”),使拥塞窗口缓慢地线性增大 。

由于跳过了 cwnd 从 1 起始的慢开始过程 ,所以被称为快恢复 。快恢复算法的实现过程如图所示,作为对比 ,虚线为慢开始的处理过程 。

在流量控制中 ,发送方发送数据的量由接收方决定 ,而在拥塞控制中,由发送方自己通过检测网络状况而决定 。实际上 ,慢开始、拥塞避免算法 、快重传和快恢复几种算法应该是同时应用在拥塞控制机制之中的 ,当发送方检测到超时的时候就采用慢开始和拥塞避免 ,当发送方接收到冗余 ACK 的时候就采用快重传和快恢复 。

在本节的最后,再次提醒读者:发送方发送窗口的实际大小由流量控制和拥塞控制共同决定。 因此,发送方实际的发送窗同大小是由 rwnd  和 cwnd  中较小的那一个确定的 。

补充问题

1. MSS 设置的太大或者太小会有什么影响

答:规定最大报文段 MSS 的大小并不是考虑到接收方的缓存可能放不下 TCP 报文段 。实际上,MSS 与接收窗口没有关系 。TCP 的报文段的数据部分,至少要加上40 字节的首部 ( TCP 首部至少 20 字节和 IP 首部至少 20 字节),才能组装成一个 IP 数据报。若选择较小的 MSS 值,网络的利用率就很低 。设想在极端情况下 ,当 TCP 报文段中只含有 1 字节的数据时 ,在 IP 层传输的数据报的开销至少有 40 字节。这样,网络的利用率就不会超过 1/41。到了数据链路层还要加 上一些开销 ,网络的利用率进一步降低 。但反过来 ,若 TCP 报文段很长 ,那么在 IP 层传输时有可能要分解成多个短数据报片 ,在终端还要把收到的各数据报片装配成原来的 TCP 报文段 。当传输有差错时 ,还要进行重传 。这些都会使开销增大 。

因此,MSS 应尽量大些 ,只要在四层传输时不要再分片就行 。由于IP 数据报所经历的路径是动态变化的 ,在一条路径上确定的不需要分片的 MSS ,如果改走另一条路径就可能需要进行分片。因此,最佳的 MSS 是很难确定的 。MSS 的默认值为 536 字节 ,因此在因特网上的所有主机都能接收的报文段长度是 536+20 ( TCP 固定首部长度) =556 字节。

2.  为什么不采用 “三次握手” 释放连接,且发送最后一次握手报文后要等待2MSL( 最长报文段寿命 ,Maximum Segment Lifetime )的时间呢

答:原因有两个 :

第一、为了保证 A 发送的最后一个确认报文段能够到达 B。如果 A 不等待 2MSL ,若 A 返回的最后确认报文段丢失 ,则 B 不能进入正常关闭状态 ,而 A 此时已经关闭 ,也不可能再重传 。

第二、防止出现 “己失效的连接请求报文段”。 A 在发送完最后一个确认报文段后 ,再经过 2MSL  可保证本连接持续的时间内所产生的所有报文段从网络中消失。造成错误的情形与不采用 “两次握手” 建立连接的原因所述的情形相同 。

注意:服务器结束 TCP 连接的时间要比客户端早一些 ,因为客户机最后要等待 2MSL 后才可以进入 CLOSED 状态。

3.  如何判定此确认报文段是对原来的报文段的确认 ,还是对重传的报文段的确认

答:由于对于一个重传报文的确认来说 ,很难分辨它是原报文的确认还是重传报文的确认  ,使用修正的 Karn  算法作为规则 :在计算平均往返时间RTT时,只要报文段重传了 ,就不采用其往返时间样本 ,且报文段每重传一次 ,就把 RTO 增大一些。

4. TCP 使用的是 GBN 还是选择重传呢

答:这是一个有必要弄清的问题。在前面讲过 ,TCP 使用累计确认 ,这看起来像是 GBN 的风格。但是,正确收到但失序的报文并不会被丢弃 ,而是缓存起来 ,并且发送冗余 ACK 指明期望收到的下一个报文段 ,这是 TCP 方式和 GBN 的显著区别。例如,A 发送了 N 个报文段 ,其中第 k ( k < N ) 个报文段丢失 ,其余 N-1 个报文段正确地按序到达接收方 B。当使用 GBN 时,A 需要重传分组 k,以及所有后继分组 k + 1,k +2 ,… ,N 。相反,TCP 却至多重传一个报文段 ,即报文段 k。另外,TCP 中提供一个 SACK ( Selective ACK ) 选项,也就是选择确认选项 。当使用选择确认选项的时候 ,TCP 看起来就和 SR 非常相似了。因此,TCP 的差错恢复机制可以看成是 GBN 和 SR 协议的混合体 。

5. 为什么超时时间发生时 cwnd 被置为 1,而收到 3 个冗余 ACK 时 cwnd 减半 ? 

答:大家可以从这个角度考虑 :超时事件发生和收到 3 个冗余 ACK ,哪个意味着网络拥塞程度更严重 ?通过分析不难发现 ,在收到 3 个冗余 ACK 的情况,网络虽然拥塞,但是至少还有 ACK 报文段能够被正确交付 。而当超时发生时 ,说明网络可能己经拥塞得连ACK 报文段都传输不了了 ,发送方只能等待超时后重传数据 。因此,超时时间发生时 ,网络拥塞更严重 ,那么发送方就应该最大限度地抑制数据发送量 ,所以 cwnd 置为 1;收到 3 个冗余 ACK 时,网络拥塞不是很严重,发送方稍微抑制一下发送的数据量即可 ,所以 cwnd 减半。

6.  为什么不采用 “两次握手” 建立连接呢 

答:这主要是为了防止两次握手情况下己失效的连接请求报文段突然又传送到服务端,而产生了错误 。考虑下面这种情况 :客户 A 向服务器 B 发出 TCP 连接请求 ,第一个连接请求报文在网络的某个结点长时间滞留 ,A 超时后认为报文丢失 ,于是再重传一次连接请求 ,B 收到后建立连接。数据传输完毕后双方断开连接。而此时,前一个滞留在网络中的连接请求到达了服务端B, 而 B 认为 A 又发来连接请求 ,此时若是使用 “三次握手”则 B 向 A 返回确认报文段 ,由于是一个失效的请求 ,因此 A 不予理睬 ,建立连接失败 。若采用的是 “两次握手”,则这种情况下 B认为传输连接已经建立 ,并一直等待 A 传输数据 ,而 A 此时并无连接请求 ,因此不予理睬 ,这样就造成了 B 的资源白白浪费了。

7.  是否 TCP 和 UDP 都需要计算往返时间 RTT?

答:往返时间 RTT 只是针对传输层 TCP 协议才很重要 ,因为 TCP 要根据 RTT 的值来设置超时计时器的超时时间 。

UDP 没有确认和重传机制 ,因此 RTT 对 UDP 没有什么意义 。

因此,不能笼统地说“往返时间 RTT 对传输层来说很重要” ,因为只有 TCP 才需要计算 RTT,而 UDP 不需要计算 RTT 。

8.   为什么 TCP 在建立连接时不能每次都选择相同的 、固定的初始序号 ?

答:1) 假定主机 A 和 B 频繁地建立连接 ,传送一些 TCP 报文段后 ,再释放连接 ,然后又不断地建立新的连接 、传送报文段和释放连接 。

2) 假定每一次建立连接时 ,主机 A 都选择相同的 、固定的初始序号,如选择 1。

3) 假定主机 A 发送出的某些 TCP 报文段在网络中会滞留较长的时间 ,以致造成主机 A 超时重传这些 TCP 报文段 。

4)假定有一些在网络中滞留时间较长的 TCP 报文段最后终于到达了主机 B,但这时传送该报文段的那个连接早己释放了 ,而在到达主机 B 时的 TCP 连接是一条新的 TCP 连接。

这样 ,工作在新的 TCP 连接的主机 B 就有可能会接收在旧的连接传送的 、已经没有意义的、 过时的 TCP 报文段 (因为这个 TCP 报文段的序号有可能正好处在现在新的连接所使用的序号范围之中),结果产生错误 。

因此,必须使得迟到的 TCP 报文段的序号不处在新的连接中所使用的序号范围之中 。 这样 ,TCP 在建立新的连接时所选择的初始序号一定要和前面的那些连接所使用过的序号不一样 。因此,不同的TCP 连接不能使用相同的初始序号。

9. 假定在一个互联网中 ,所有的链路的传输都不出现差错,所有的结点也都不会发生故障。 试问在这种情况节 ,TCP 的 “可靠交付” 的功能是否就是多余的

答:不是多余的 。TCP  的 “可靠交付” 功能在互联网中起着至关重要的作用 。至少在以下列举的情况下, TCP 的 “可靠交付” 功能是必不可少的 。

1) 每个 IP 数据报独立地选择路由 ,因此在到达目的主机时有可能出现失序 。

2)  由于路由选择的计算出现错误 ,导致IP 数据报在互联网中转圈 。最后数据报首部中的生存时间 TTL 的数值下降到零。这个数据报在中途就被丢失了 。

3) 在某个路由器突然出现很大的通信量 ,以致路由器来不及处理到达的数据报 。因此有的数据报被丢弃 。

以上列举的问题表明了 :必须依靠 TCP “可靠交付” 功能才能保证在目的主机的目的进程中接收到正确的报文。

本节的知识架构图如下 :

上一篇下一篇

猜你喜欢

热点阅读