1. 运输层概述
运输层(Transport Layer):运输层位于应用层和网络层之间,是分层网络体系结构的重要部分。TCP(Transmission Control Protocol)和UDP(User Datagram Protocol)即是运输层协议。运输层协议为运行在不同主机上的应用进程之间提供了逻辑通信(logic communication) 的功能。
从应用程序的角度看,通过逻辑通信,运行不同进程的主机好像直接相连一样,但是实际上这些主机可能分布在地球的两侧并通过许多路由器或其他多种不同类型的链路相连。运输层的存在使得应用程序可以使用其提供的逻辑通信功能彼此发送报文而无需考虑承载这些报文的物理基础设施。
运行层协议是在端系统(end systems,即电脑、手机等终端)中而不在路由器(network routers)中实现。
在发送端,运输层将从发送应用程序进程接收到的报文转换成运输层分组,即运输层报文段(segment) 。转换为运输层报分组的方法一般是将应用报文划分为较小的块,然后为每块加上运输层首部生成运输层报文段。运输层将其传递给网络层,网络层将其封装成网络层分组(数据报,datagram)并向目的地发送。注意:在网络层,路由器仅作用于该数据报的网络层字段,不会检查封装在该数据报的运输层报文段的字段。在接收端,网络层从数据报中提取运输层报文段,并将报文段向上提交给运输层。
1.1 运输层与网络层的关系
运输层位于网络层之上,因此运输协议能够提供的服务常常受限于底层网络层协议的服务模型。网络层提供了主机之间的逻辑通信,而运输层提供了不同主机的进程之间的逻辑通信。运输层只工作在端系统中,即手机、电脑等非链路设备中。
1.2 因特网运输层服务总览
因特网为应用层提供了两种可用的运输层协议:
- UDP(User Datagram Protocol):不可靠、无连接的服务
- TCP(Transmission Control Protocol):可靠的、面向连接的服务(connection-oriented)
运输层分组一般称为报文段(segment),然而,一些文献中,将TCP的分组称为报文段,而将UDP的分组称为数据报(datagram),为了避免和网络层的数据报混淆,将TCP和UDP的分组都称为报文段(segment)。
网络层协议有一个名字IP(Internet Protocol,网际协议) ,为主机之间提供逻辑通信。IP的服务模型是尽力而为交付的服务(best-effort delivery service),这意味着网络层会尽最大努力在通信的主机之间交付报文段,但不做任何确保:
- 不确保报文段的交付
- 不确保报文段的交付顺序
- 不确保报文段数据的完整性
因为这些不确保,网络层协议(IP协议)也被称为不可靠服务(unreliable servie)。
UDP和TCP的服务模型是:将两个端系统间IP的交付服务扩展为运行在端系统上两个进程之间的交付服务。将主机间交付扩展到进程间交付被称为运输层的多路复用(transport-layer multiplexing)和多路分解(demultiplexing)。UDP和TCP还可以在其报文段首部中包含差错检测字段而提供完整性检查。
进程到进程的数据交付和差错检测是两种最低限度的运输层服务,UDP提供的也就仅这两种,与IP协议一样,UDP也是一种不可靠服务,不能保证一个进程所发送的数据能够完整无缺到达目的进程。
TCP除了基本的两种服务外,还提供了附加服务。首先是可靠数据传输(reliable data transfer),通过使用流量控制(flow control)、序号(sqquence numbers)、确认(acknowledgments)和定时器(timers),可以确保正确地、按序地将数据从发送进程交付给接收进程。TCP还提供拥塞控制(congestion control), 力求为每个通过一条拥塞网络链路的连接平等的共享网络链路带宽。
2. 多路复用与多路分解
多路复用(transport-layer multiplexing)和多路分解(demultiplexing) :将网络层提供的主机到主机交付服务延伸到为运行在主机上的应用程序提供进程到进程的交付服务。多路复用与多路分解是所有计算机网络都需要的。
一个进程有一个或多个套接字(socket),套接字相当于从进程向网络传递数据和从网络向进程传递数据的通道(门户)。在接收端运输层实际上并没有直接将数据交付给进程,而是将数据交付给一个中间的套接字。
如何将一个到达的运输层报文段定向到合适的套接字(套接字被特定进程拥有)即是多路分解的作用。运输层报文段将具有几个字段,在接收端,运输层检查这些字段,标识出接收套接字,进而将报文段定向到相应套接字。
1. 多路分解(demultiplexing):将运输层报文段中的数据交付到正确的套接字。
2. 多路复用(multiplexing):在源主机从不同套接字中收集数据块,并为每个数据块封装上首部信息(有些首部用于后面分解)从而生成报文段,然后将报文段传递至网络层。
运输层多路复用的基本要求:① 套接字有唯一标识符;② 每个报文段有特殊字段来指示该报文段所要交付到的套接字。
如下图所示,有源端口号字段(source port number field) 和目的端口号字段(destination port number field),端口号是一个16比特数,大小在0~65535之间(0 ~ 216)。其中0~1023端口是周至端口(well-know port number)。
如今的高性能Web服务通常只使用一个进程,但是为每个客户连接创建一个具有新连接套接字的新线程。UDP无非就是对网络层协议增加了多路复用和多路分解服务而已。
附:端口扫描工具nmap https://nmap.org/
3. UDP-无连接传输
UDP只做运输层协议能做的最少工作,除复用/分解功能和少量的差错检测外,几乎没有对IP增加别的东西。
UDP相较于TCP的优势:
- 应用层可以更精准的控制发送什么数据及何时发送数据
采用UDP时,只要应用层进程将数据传递给UDP,UDP就会将数据打包进UDP报文段并立即传输给网络层。 而TCP将有一个拥塞控制机制。TCP的拥塞控制会导致如网络电话、视频会议之类的实时应用应用性能变得很差。 - 无需建立连接
TCP在传输之前要经过三次握手,而UDP无需任何准备即可开始传输,因此UDP不会引入建立连接的时延。这也是DNS运行在 UDP之上而不是TCP之上的主要原因,若运行在TCP上,DNS会慢得多。另外,用于谷歌Chrome浏览器中的QUIC协议,即是将UDP作为支撑并在UDP之上的应用层协议实现可靠性。 - 无连接状态
TCP需要在端系统中维护连接状态,该状态包含接收和发送缓存、拥塞控制参数以及序号与确认号的参数,而UDP无需维护这些状态参数。 - 分组首部开销小
每个TCP报文段都有20字节的首部开销,而UDP仅有8字节的开销。
一些流行应用的运输层协议:
应用 | 应用层协议 | 运输层协议 |
---|---|---|
电子邮件 | SMTP | TCP |
远程终端访问 | Telnet | TCP |
Web | HTTP | TCP |
文件传输 | FTP | TCP |
远程文件服务器 | NFS | 一般UDP |
流式多媒体 | 专用协议 | UDP或TCP |
网络电话 | 专用协议 | UDP或TCP |
网络管理 | SNMP | 一般UDP |
域名转换 | DNS | UDP |
注意:通过在应用层建立可靠性机制即可实现使用UDP的可靠数据传输,也就是说应用进程可以进行可靠通信而无需受限于由TCP拥塞控制机制强加的传输速率限制。
3.1 UDP报文段结构
UDP首部只有4个字段,每个字段由两个字节组成。长度字段指示了在UDP报文段中的字节数(首部加数据的字节数)。
3.2 UDP检验和
UDP检验和提供了差错检测的功能。检验和用于确定UDP报文段从源到达目的地移动时,其中的比特是否发生了改变(如链路中的噪声干扰或存储在路由器中发生错误)。
发送方的UDP对报文段中的所有16比特字节的和进行反码运算,求和时遇到的任何溢出都被回卷,得到的结果作为检验和字段。简单来说就是将首部和数据字段每16比特求和后求反码。
回卷:将进位加到最低位。
UDP提供检验和的原因:基于端到端的原则(end-end principle) ,即使许多链路层协议提供了差错检测,但是由于无法得知传输的过程中所使用的协议都具有差错检测的功能,及在无法确保路由器存储过程中差错检测的情况,因此必须在端到端的基础上在运输层提供差错检测。端到端原则:与在较高级别提供这些功能的代价相比,在较低级别设置这些功能可能时冗余或毫无意义的。
此外,虽然UDP提供差错检测,但对差错没有任何恢复能力,一般的做法是简单的丢弃出错报文段,另一种做法是将受损报文段提交应用程序并发出警告。
4. 可靠数据传输原理
为了实现可靠数据传输,我们构造了一种可靠数据传输的抽象,数据可以通过一条可靠的信道进行传输。实现可靠数据传输抽象的是可靠数据传输协议(reliable data transfer protocol) 。
此节后文的讨论基于假设:1. 较低层是不可靠的点对点信道;2. 仅考虑单项数据传输(unidirectional data transfer) 的情况。双向数据传输(即全双工,full-duplex)从概念上不会更难。
4.1 构造可靠数据传输协议
4.1.1 RDT1.0 经完全可靠信道的可靠数据传输
考虑最简单的情况,即底层信道是完全可靠的,下图展示了RDT1.0 发送方和接收方的有限状态机(Finite-State Machine,FSM) 。
在FSM中:
- 初始状态用虚线箭头表示;
- 实线箭头表示协议从一个状态变迁到另一个状态;
- 引起变迁的事件显示在横线上方;
- 若一个事件没有动作或没有事件就采取了一个动作则使用Λ表示缺少事件或动作。
4.1.2 RDT2.0 经具有比特差错信道的可靠数据传输
底层信道更加实际的模型是分组中的比特可能受损。在分组的传输、传播和缓存的过程中,比特差错常常出现在网络的物理部件中。假定所有发送的分组将按其发送的顺序被接收,即使比特可能受损。
考虑日常说话时,如果听清楚了会有肯定回应,如果没听清会让你重新说一遍。在设计RDT2.0时,可以在报文中使用肯定确认(positive acknowledgment) 和否定确认(negative acknowledgment) 实现,使得接收方可以让发送方知道哪些内容有误需要重传。
基于以上重传机制的可靠数据传输协议称为自动重传请求协议(Automatic Repeat reQuest,ARQ) 。ARQ还需要三种机制来处理存在的比特差错:
- 差错检测:需要使接收方能检测出何时出现了比特错
- 接收方反馈:如接收方使用肯定确认(ACK)和否定确认(NAK)反馈
- 重传:接收方收到有差错的分组时,发送方重传该分组。
注意上图:当发送方处于等待ACK或NAK的状态时,它不能从上层获得更多的数据;这就是说,rdt_send()事件不可能出现;仅当收到ACK并离开该状态时才能发送这样的事件。因此,发送方不会发送新数据,除非发送方屈辱接收方正确接收了当前分组,RDT2.0此种协议称为停等协议(stop-and-wait)。
RDT2.0的致命缺陷是没有考虑ACK和NAK分组可能受损,解决该问题的简单方法是:在数据分组中添加一新字段,让发送方对其数据分组编号,即将发送数据分组的序号(sequence number)放在该字段中,接收方只需要检查需要即可确定接收的分组是否一次重传。
RDT2.2实现了在有比特差错信道上的一个无NAK的可靠数据传输协议
4.1.3 RDT3.0 经具有比特差错、丢包信道的可靠数据传输
现在假定除了比特受损外,底层信道还会丢包,我们让发送方负责检测和恢复丢包工作。一般通过发送方等待足够长的时间未接收到接收方的响应,则重传该分组。
但发送方需要等待多久才能确定分组或ACK丢失了呢?答案是发送方和接收方之间的往返时延加上接收方处理一个分组所需的时间。 即 往返时延 + 接收方处理一个分组的时间。为了实现该基于时间的重传机制,需要一个倒计数定时器(countdown timer) ,在给定时间过去后,中断发送方并执行相应操作。发送方需要:① 每发送一个分组(包括重传分组)时,便启动一个定时器;② 响应定时器中断;③ 接收到正确响应时终止定时器。 接收方仍然使用RDT2.2即可,因为RDT2.2已经有序号来处理冗余分组情况。
下图展示了RDT3.0在无丢包、分组丢失、丢失ACK、过早超时情况下的图示,因为分组序号在0和1之间交替,因此RDT3.0有时被称为比特交替协议(alternating-bit protocol):
在检验和、序号、定时器、肯定和否定确认分组这些技术中,每种机制都在协议的运行中起到了必不可少的作用。至此,我们得到了一个可靠数据传输协议。
4.2 流水线可靠数据传输协议
RDT3.0虽然已经实现了在有比特差错、丢包情况下的可靠数据传输协议,但由于其是一个停等协议,难以取得较好的性能。
假设两端系统之间的光速往返传播时延RTT为30毫秒,通过一条发送速率R为1Gbps(每秒109比特)的信道相连,包括首部和数据的分组长L为1000字节(8000比特),则发送一个分组进入1Gbps链路实际所需时间为:
- ttrans = L/R = 8000 bit/pkt /109 bit/s = 8μs/pkt
因此,在8μs后,最后1比特数据进入发送端信道,该分组将经过半个RTT(15ms)后到达接收端,最后1比特在15.008ms到达接收方。
假设ACK分组很小(可以忽略发送时间),发送方将在30.008ms时接收到ACK。在整个30.008ms过程中,发送方实际的发送时间只有0.008ms。
发送方或信道的利用率(utilization):发送方实际忙于将发送比特送进信道的那部分时间与发生时间之比。 以上过程发送方的利用率:
- Usender = (L/R) / (RTT + L/R) = 0.008 / 30.008 = 0.00027
也就是说发送方只有万分之2.7的时间是忙于在发生数据的。
流水线(pipelining):不以停等方式运行,允许发送方发送多个分组而无需确认。流水线工作需要以下技术支撑:
- 必须增加序号范围:因为每个发生中的分组必须有唯一的序号。
- 发送方和接收方都必须缓存多个分组:发生方最低限度缓存那些已发送但没有确认的分组;接收方或许需要缓存那些已经正确接收的分组。
- 需要的序号范围和缓存的要求取决于协议如何处理丢失、损坏和延时过大的分组。
解决流水线的差错恢复有两种基本方法:回退N步(Go-Back-N,GBN) 和选择重传(Selection Repeat, SR)。
4.3 回退N步
在回退N步(GBN)协议中,允许发送方发送多个分组而无需等待确认,但受限于在流水线中未确认的分组数不能超过最大允许数N。
基序号(base):最早未确认分组的序号;下一个序号(nextseqnum):最小未使用序号(即下一个待发分组的序号)。
GBN常被称为滑动窗口协议(sliding-window protocol) ,N被称为窗口长度(window size) 。
如果承载分组序号的首部字段的比特数为k,则该序号的范围为[0, 2k-1]。在一个有限的序号范围内,所有涉及序号的运算必须使用模2k运算,以此将序号空间形成一个长度为2k的环。
GBN协议的主要要点:
- 如果窗口未满(没有发送N个分组),则产生分组并发送;如果窗口已满,将数据返回上一层表示窗口已满,上层将等待一会再次尝试。
- 累计确认(cumulative acknowledgment) :表明接收方已正确接收到序号为n之前的包括n的所有分组。
- 定时器用于恢复数据和确认分组的丢失,如果出现超时,发送方将重传所有已发送但还未被确认过的分组。
- 在接收方,如果一个序号为n的分组被正确接收到,则n之前的所有分组都应已经正确接收并交付上层,此时接收方为分组n发送ACK并将数据交付上层,在其他所有情况,接收方丢弃该分组,并未最近按序接收的分组重新发送ACK。
- 在GBN中,接收方会丢弃所有失序分组,不会将其缓存。
下图的窗口长度N为4,发送方在发送分组3之后必须等待一个或多个ACK后发送后面的分组
GBN可以使用基于事件的编程(event-based programming),其中的过程要么被协议栈中的其他过程调用,要么作为以此中断的结果。
4.4 选择重传
GBN显而易见的缺陷是重传机制:如果窗口长度为1000,那么只要1000中有一个出错(不在尾部),那么窗口内很多分组将反复重传。
选择重传(Selection Repeat,SR)协议:通过让发送方仅重传那些其怀疑在接收方出错的分组而避免不必要的重传。SR也会使用到窗口长度N来限制流水线中未完成、未确认分组的个数。
在SR中,失序的分组将会被缓存知道所有丢失分组(即序号更小的分组)全被接收为止。
5. TCP-面向连接的传输
TCP是运输层面向连接的可靠的运输协议,包含差错检测、重传、累计确认、定时器及用于序号和确认号的首部字段。
5.1 TCP连接
TCP被称为是面向连接的(connection-oriented) ,两个应用进程在开始发送数据之前,会通过发送一些预备报文段(握手过程)来初始化TCP连接相关的一些参数。TCP连接不像电路交换中TDM或FDM,TCP的连接是一种逻辑连接,其共同状态仅保留在两个通信系统的TCP程序中。因为TCP协议只在端系统中运行,所以所有中间网络元素不会维持TCP连接状态,实际上中间路由器对TCP连接压根不知情或者说视而不见。
TCP连接提供的是全双工服务(full-duplex service) :如果一台主机上的进程A与另一台主机上的进程B存在一条TCP连接,那么应用层数据就可以在B进程和A进程之间互相流动。
TCP连接总是点对点的(point-to-point) :单个发送方与单个接收方之间的连接,在TCP连接中无法实现“多播”的情况。
TCP连接的基本过程:客户端首先发送一个特殊的TCP报文段,服务器用另一个特殊的TCP报文段来响应,最后,客户端再用第三个特殊报文段作为响应。前两个报文段不承载“有效载荷”,即不包含应用层数据,第三个报文段可以承载有效载荷。由于发生了3个报文段,这种连接过程也被称为三次握手(three-way handshake) 。
一旦建立起一条TCP连接,两个应用进程之间就可以互相发送数据了,TCP将应用层数据引导进入该链接的发送缓存(send buffer) ,发送缓存是在三次握手时设置的缓存之一。TCP会不时从缓存取出数据并传递到网络层。TCP可以从缓存中取出并放入报文段中的数据数量取决于最大报文段长度(Maximum Segment Size,MSS),而MSS通常根据最初确定的有本地发送主机发送的最大链路层帧长度(即最大传输单元 Maximum Transmission Unit,MTU)来设置。MSS的长度要保证单个TCP报文段(当封装在一个IP数据报中)加上TCP/IP首部长度(通常40字节)时将适合单个链路层帧长度。以太网和PP链路层协议都具有1500字节的MTU,因此MSS的典型值为1460。注意:MSS指的是在报文段里应用层数据的最大长度。
5.2 TCP报文字段
TCP报文段由首部字段和数据字段组成,数据字段来自应用层数据,MSS限制了报文段数据字段的最大长度,发送数据时,TCP通常先将数据划分成长度为MSS的若干块。
TCP首部一般是20字节(比UDP多12字节),首部包含源端口号和目的端口号,用于多路分解/复用,还有其他许多字段。
TCP报文段除了端口号、检验和外还包含有:
- 32比特序号字段(sequence number field) 和32比特的确认号字段(acknowledgment number field) ,用于实现可靠数据传输服务。
- 16比特接收窗口字段(receive window field) ,用于流量控制。
- 4比特首部长度字段(header length field) ,用于指示首部字段长度,因为TCP存在选项字段。
- 可选的选项字段(options field),用于发送方和接收方协商最大报文段长度(MSS)是,或在高速网络环境下用作窗口调节因子时使用。
- 6比特标志字段(flag field) ,标志字段包含ACK、RST、SYN、FIN、CWR、ECE、URG等标志,其中ACK比特用于指示确认字段中的值时有效的,即该报文段包括一个对已被成功接收报文段的确认。RST、SYN和FIN比特用于连接的建立与关闭。CWR和ECE用于明确拥塞通告。PSH用于指示接收方应立即将数据交给上层。URG比特用于指示报文段里存在着被发送端的上层置为“紧急”的数据。
- 16比特紧急数据指针字段(urgent data pointer field) ,当URG被置为紧急时,指向紧急数据。
在实际中,PSH、URG和紧急数据指针并没被使用。
TCP将数据看成一个无结构、有序的字节流,序号建立在传输的字节流之上,报文段的序号是该报文段首字节的字节流编号。
假设主机A已收到来自主机B的编号为0到535的所有字节,同时打算发送一个报文段给主机B,主机A就会将536填入发送给主机B报文段的确认号字段中。即接收端主机将会把期望接收的下一个字节的序号填入确认号字段中 。因为TCP只确认该流中到第一个丢失字节为止的字节,所有TCP被称为提供累计确认(cumulative acknowledgment) 。
在TCP中,如果一个序号较大的报文段先于一个序号较小的报文端达到接收端,即失序达到,接收方会保留失序的字节,并等待缺少的字节已填补该间隔 。
5.3 往返时间的估计与超时
TCP与前述RDT协议一样,采用超时重传机制来处理报文段丢失的问题。超时间隔长度如何设置,超时间隔必须大于该链接的往返时间(RTT),即一个报文段发出到其被确认的时间。
5.3.1 往返时间估计
报文段的样本RTT(SampleRTT)就是从某报文段被发出(即交给IP协议)到该报文段的确认被收到之间的时间量。
显然,由于路由器的拥塞和端系统负载的编号,报文段的SampleRTT值会随之波动,TCP会维持一个SampleRTT的均值(EstimateRTT) ,一旦有新的SampleRTT,TCP将会根据下列公式来更新EstimateRTT:
- EstimateRTT = (1 - α)* EstimateRTT + α * NewSampleRTT
即EstimateRTT的新值是由以前的EstimateRTT值与NewSampleRTT加权组合在一起而成的。α的推荐值为α = 0.125(八分之一):
- EstimateRTT = (1 - 0.125)* EstimateRTT + 0.125 * NewSampleRTT
上述公式对最近样本的权值要大于对旧样本的权值,因为越近的样本越能更好的反映网络的当前拥塞情况。从统计学来说,这种平均称为指数加权移到平均(Exponential Weighted Moving Average,EWMA) 。
DevRTT(RTT偏差) 用于估算SampleRTT一般会偏离EstimateRTT的程度:
- DevRTT = (1 - β) * DevRTT + β * | SampleRTT - EstimateRTT |
β的推荐值为0.25,DevRTT是SampleRTT和EstimateRTT之间差值的EWMA。
5.3.2 设置和管理重传超时间隔
假设已经给出了EstimateRTT和DevRTT,超时间隔应该大于等于EstimateRTT,否则将造成不必要的重传。但也不应该比EstimateRTT大很多,否则将导致重传时间间隔很长。超时间隔应为EstimateRTT加上一定余量,当SampleRTT波动大时,余量应该大一些;波动较小时,余量应该小一些。得到以下公式:
- TimeoutInterval = EstimateRTT + 4 * DevRTT
推荐的TimeoutInterval初始值为1秒,当出现超时后,TimeoutInterval值将加倍,只要收到报文段并更新EstimateRTT,就会使用上述公式计算TimeoutInterval。
5.4 可靠数据传输
IP不保证数据报的交付,不保证数据报的按序交付,不保证数据报中数据的完整性。TCP在IP不可靠的尽力而为的服务之上创建了一种可靠数据传输服务(reliable data transfer service) 。TCP的可靠数据传输服务确保一个进程从其接收缓存中读出的数据流是无损坏、无间隙、非冗余和按序的数据流,即数据流在网络中的传输是可靠的。
之前设想为每一个已发送但未被确认的报文段设立定时器,但如此定时器的管理需要相当大的开销。TCP仅使用单一的重传定时器。
下图给出了TCP发送方的高度简化的描述,其中的主要事件是:从上层接收数据、定时器超时、收到ACK:
- 当TCP接收到上层数据时,会将数据封装进入报文段,传递给IP,此时TCP将会启动定时器,过期时间为TimeoutInterval。
- 当定时器超时,TCP将重传引起超时的报文段来响应超时事件,然后重启定时器。
- 当收到ACK时,TCK将ACK的值y与SendBase比较,SendBase是TCP状态变量最早未被确认的序号,因此SendBase - 1时接收方已正确按序收到的数据的最后一个字节的序号,如果y > SendBase ,则该ACK是在确认一个或者多个先前未被确认的报文段,此时更新SendBase;如果当前有未被确认的报文段,TCP将重启定时器。
TCP的差错恢复机制是一种GBN协议和SR协议的混合体,是一种选择确认(selective acknowledgment)。
5.5 流量控制
TCP链路的两侧都设置了接收缓存,但有一个实际问题是,接收方应用也许正忙于其他任务,甚至要过很长时间后才去读取该数据。如果读取数据时相对缓慢,而发送方发送得太多、太快,发送的数据就会很容易使该连接的接收缓存溢出。
TCP为应用程序提供了流量控制服务(flow-control service) 以消除发送方使接收方接收缓存溢出的可能性。流量控制是一个速率匹配服务。不同于TCP的拥塞控制,不要混淆。
TCP通过让发送方维护一个称为接收窗口(receive window)的变量来提供流量控制。接收窗口用于给发送方一个指示——该接收方还有多少可用的缓存空间。
定义以下变量:
- LastByteRead:接收方的应用进程从缓存读出的数据流的最后一个字节的编号
- LastByteRcvd:从网络中达到并以存放入主机B的接收缓存的数据流的最后一个字节编号
由于TCP不允许已分配的缓存溢出,因此:
- LastByteRcvd - LastByteRead <= RcvBuffer
接收窗口用Rw表示:
- Rw = RcvBuffer - [ LastByteRead - LastByteRcvd ]
5.6 TCP连接管理
TCP协议如何建立和拆除一条TCP连接,这很重要,因为TCP连接的建立会显著地增加人们感受到时延。
建立一条TCP连接需要以下三个步骤:
- 客户端的TCP首先向服务端的TCP发送一个特殊的TCP报文段,该报文段不含应用层数据,其SYN比特被置为1,此外,客户端会随机选择一个初始序号(client_isn),并将client_isn放入序号字段。此报文段被称为SYN报文段。
- 服务端从IP数据报中提取TCP报文段,并为该TCP连接分配TCP缓存和变量(使得TCP易遭受SYN洪泛的拒绝服务攻击),并向客户端发送允许连接的报文段,该报文段不含应用层数据,其SYN比特被置为1,确认号字段被置为client_isn + 1,此外,服务端会选择一个初始序号(server_isn),并将server_isn放入序号字段。此报文段被称为SYNACK报文段。
- 收到SYNACK报文段后,客户端会给该连接分配缓存和变量,向服务器发送一个报文段,该报文段含有应用层数据,因为连接已经建立,SYN被置为0,将server_isn + 1放入首部确认字段完成服务器允许连接的确认。
此时TCP连接建立成功,以后每一个报文段中,SYN都将置为0。由于建立连接的过程中主机之间发送了3个分组,因此这种创建连接的过程称为3次握手(three-way handshake) 。
关闭一条连接需要以下4个步骤:
- 客户端TCP向服务端发送一个特殊的TCP报文段,该报文段的FIN比特被置为1。
- 服务端收到该报文段后,向客户的发送一个确认报文段(ACK)。
- 服务端发送其终止报文段,其FIN比特置为1。
- 最后,客户端对这个服务端终止报文段进行确认(ACK)。
此时,连接被关闭,两台主机上用于该连接的所有资源都被释放了。
下图展示了TCP协议在各种TCP状态(TCP state)之间的变迁:
- 客户端:
- 服务端:
附:SYN洪泛攻击
TCP的连接管理协议为经典的DoS(拒绝服务)攻击即SYN洪泛攻击(SYN flood attact) 提供了环境。
一种有效的防御系统称为SYN cookie ,其被部署在大多数主流操作系统中,其工作方式大概是这样的:
- 当收到SYN报文段时,服务器生成一个初始TCP序列号放入序号字段,该序列号是SYN报文段的源和目的IP地址与端口号已经仅有该服务器才知道的密码的一个复杂函数(散列函数)。这种序列号被称为cookie,服务器发送这种具有特殊初始序列号的SYNACK分组。注意:服务器并不需要记忆该cookie或任何其他信息。
- 当收到合法的客户端返回的ACK报文段时,只需要利用之前的散列函数将ACK报文段中的确认字段 - 1解出源和目的IP地址及端口号即可,因为返回的确认字段中的值为server_isn +1。
- 如果客户端没有返回ACK报文段,则初始的SYN并没有对服务端造成任何危害,因为服务器没有为其分配任何资源。
6. 拥塞控制原理
6.1 拥塞的原因和代价
主要有以下两点原因:
- 路由器缓存溢出照成的丢包
- 发送速率远远大于链路中的传输速率,造成许多包在路由器缓存中排队
主要代价有以下4点:
- 当分组的达到速率接近链路容量时,分组会经历巨大的排队时延(拥塞导致延迟)
- 发送方必须重传以补偿因为缓存溢出而导致的丢包(拥塞导致重传)
- 发送方在遇到大时延时所进行的不必要重传会引起路由器利用其链路带宽来转发不必要的分组副本(一个正常分组发了两次)
- 在多跳的链路中,有些路由器会因为转发到下一个十分忙碌的路由器(该路由器将收到的包直接丢弃)而做了无用功,浪费了传输容量
6.2 拥塞控制方法
主要有两种控制方法:
- 端到端拥塞控制:网络层不为运输层提供显示支撑,端系统通过对网络行为(是否有丢包、大延迟)的观察来推断。
- 网络辅助的拥塞控制:路由器向发送方提供网络中的拥塞状态的显示反馈信息。一种形式是采用发送阻塞分组(choke packet) 的形式通知拥塞情况,一种形式是路由器标记或更新从发送方流向接收方的分组中的某个字段来指示拥塞的产生。例如在ATM可用比特率(Avaliable Bite Rate,ABR)拥塞控制中,路由器显示地通知发送方路由器能在输出链路上支持的最大主机发送速率。
TCP采用端到端的方法解决拥塞控制,TCP报文段的丢失(通过超时或3次冗余ACK得知)被认为是网络拥塞的一个迹象,TCP会相应减小其窗口长度。最近IP和TCP也能选择性的实现网络辅助拥塞控制。
7. TCP的拥塞控制
由于IP层并不会向端系统提供显示的网络拥塞反馈,TCP必须使用端到端拥塞控制而不是使用网络辅助的拥塞控制。
TCP拥塞控制的主要思想是:让网络中每个发送方根据所感知到的网络拥塞程度来限制其能向连接发送流量的速率。
其中主要要解决以下3个问题:
- TCP发送方如何限制其向连接发送流量的速率
发送方TCP会维护一个用于拥塞控制机制的额外变量,即拥塞窗口(congestion window) ,TCP通过控制其大小来限制向连接中发送的速率。在一个发送方中未被确认的数量不会超过cwnd(拥塞窗口)和rwnd(接收窗口)中较小的那一个:
- LastByteSent - LastByteAcked <= min
-
TCP发送方如果感知链路中的拥塞
TCP发送方的丢包事件定义为:超时或收到接收方的3次冗余ACK(快速重传) 。当出现丢包时,就能推断出在这条路径上有因为过度拥塞的情况而遭受路由器缓存溢出,此时发送方就认为发送方到接收方的路径上出现了拥塞指示。 -
感知到拥塞时,采用何种算法来改变发送速率
设计此种算法有三个根本原则:
- 丢包意味着拥塞,理应降低发送速率
- 当先前未确认报文段的确认(ACK)到达时,预示一切顺利,理应增加发送速率
- 要有带宽探测:发送通过增加传输速率,从该速率后退,进而再次开始探测,看看拥塞开始速率是否发生了变化(本质上是试探出链路带宽)
TCP拥塞控制算法(TCP congestion control algorithm)
TCP拥塞控制算法主要包含三部分:慢启动、拥塞避免、快速恢复 。其中慢启动和拥塞避免时TCP强制部分,快速恢复是推荐部分。
- 慢启动
当一条TCP连接开始时,cwnd的值通常初始置为一个MSS的较小值,使得初始发生速率大约为MSS/RTT。这是一个很小的发生速率。TCP发生方希望迅速找到可用带宽数量,在慢启动(slow-start)中,cwnd的值以1个MSS开始并且每当传输的报文段首次被确认就增加1个MSS。这种机制在顺利的情况下使得每过1个RTT,发送速率就翻倍。在慢启动阶段发生速率是指数级增长。
慢启动结束有3种情况:① 出现超时或丢包(拥塞)时,TCP发送方将cwnd置为1个MSS并重新开始慢启动过程,另外,将ssthresh(“慢启动阈值”的速记)变量置为cwnd的一半。② 当ssthresh有值,且cwnd等于ssthresh时,结束慢启动并进入拥塞避免模式。③ 如果检测到3个冗余ACK,TCP将执行快速重传并进入快速恢复状态 。 - 拥塞避免
一旦进入拥塞避免状态,cwnd的值大约是上次遇到拥塞时的值的一半,此时的方案是,每收到一个新的确认(ACK)就将cwnd增加一个 MSS/cwnd 字节。当出现超时或丢包时,cwnd将被设置为1个MSS然后进入慢启动阶段,ssthresh的值被更新为cwnd的值的一半。 - 快速恢复
在快速恢复种,对于引起TCP进入快速恢复状态的缺失报文段收到的每个冗余ACK,cwnd的值增加一个MSS。(In fast recovery, the value of cwnd is increased by 1 MSS for every duplicate ACK received for the missing segment that caused TCP to enter the fast-recovery state. )最后,当丢失报文段的最后一个ACK达到时,TCP在降低cwnd后进入拥塞避免。当出现丢包或超时时,cwnd被置为1个MSS,ssthresh被置为cwnd的一半并进入慢启动阶段。[此段中文译本翻译像坨屎] TCP Tahoe是TCP的早期版本,没有快速恢复的机制,TCP的较新版本TCP Reno综合了快速恢复:
下图是TCP拥塞控制算法的完整FSM描述: