面向连接的运输:TCP
TCP是因特网运输层的面向连接的可靠的运输协议。我们在本节中将看到,为了提供可靠数据传输,TCP依赖于一节所讨论的许多基本原理,其中包括差错检测、重传、累积确认、定时器以及用于序号和确认号的首部字段
一、Tcp连接
1.发送缓存
一旦建立起一条TCP连接,TCP将这些数据引导到该连接的发送缓存(sendbuffej里,发送缓存是发起三次握手期间设置的缓存之一。
2.最大报文段长度
TCP可从缓存中取出并放入报文段中的数据数量受限于最大报文段长度(Maximum Segment Size,MSS)。MSS通常根据最初确定的由本地发送主机发送的最大链路层帧长度(即所谓的最大传输单元(Maximum Transmission Unit, MTU))来设置。设置该MSS要保证一个TCP报文段(当封装在一个IP数据报中)加上TCP/IP首部长度(通常40字节)将适合单个链路层帧。以太网和PPP链路层协议都具有1500字节的MTU,因此MSS的典型值为1460字节。
3.Tcp发送和接收缓存
TCP连接的组成包括:一台主机上的缓存、变量和与进程连接的套接字,以及另一台主机上的另一组缓存、ll变量和与进程连接的套接字。
二、TCP报文段结构
•首部包括源端口号和目的端口号,它被用于多路复用/分解来自或送到上层应用的数据。
• 32比特的序号字段(sequence number field)和32比特的确认号字段(acknowl
edgment number field) 这些字段被TCP发送方和接收方用来实现可靠数据传输服
务
• 16比特的接收窗口字段(receive window field),该字段用于流量控制。我们很快
就会看到,该字段用于指示接收方愿意接受的字节数量。
• 4比特的首部长度字段(header length field),该字段指示了以32比特的字为单位
的TCP首部长度。由于TCP选项字段的原因,TCP首部的长度是可变的。(通常,
选项字段为空,所以TCP首部的典型长度是20字节。)
•可选与变长的选项字段(options field),该字段用于发送方与接收方协商最大报文
段长度(MSS)时,或在高速网络环境下用作窗口调节因子时使用。首部字段中
还定义了一个时间戳选项。可参见RFC 854和RFC 1323 了解其他细节。
• 6比特的标志字段(flag field) ACK比特用于指示确认字段中的值是有效的,即
该报文段包括一个对已被成功接收报文段的确认。RST、SYN和FIN比特用于连
接建立和拆除。在明确拥塞通告中使用了 CWR和
ECE比特,如3.7.2节中讨论的那样。当PSH比特被置位时,就指示接收方应立
即将数据交给上层。最后,URG比特用来指示报文段里存在着被发送端的上层实
体置为“紧急”的数据。紧急数据的最后一个字节由16比特的紧急数据指针字段
指出。当紧急数据存在并给出指向紧急数据尾指针的时候,TCP必须通知接收端的上层实体。(在实践中,PSH、URG和紧急数据指针并没有使用。为了完整性起见,我们才提到这些字段。)
三、序号和确认号
TCP把数据看成一个无结构的、有序的字节流。我们从TCP对序号的使用上可以看出
这一点,因为序号是建立在传送的字节流之上,而不是建立在传送的报文段的序列之上
假定数据流由一个
包含500 000字节的文件组成,其MSS为1000字节,数据流的首字节编号是0。
从主机B到达的每个报文段中都有一个序号用于从B流向A的数据。
主机A填充进报文段的确认号是主机A期望从主机B收到的下一字节的序号。
举例1
假设主机A已收到一个来自主机B的包含字节0 ~535的报文段,A到B的下一个报文段将在确认号字段中包含536。TCP只确认该流中第一个丢失字节为止的字节,所以TCP被称为提供累积确认
举例2 telnet(远程登录协议)
Seq(序号),ACK(确认号)
第三个报文段是从客户发往服务器的。它的唯一目的是确认已从服务器收到的数据。
四、流量控制
1.一条TCP连接的每一侧主机都为该连接设置了接收缓存。如果某应用程序读取数据时相对缓慢,而发送方发送得太多、太快,发送的数据就会很容易地使该连接的接收缓存溢出
2.流量控制服务与为拥塞控制区别
流量控制: 此是一个速度匹配服务,即发送方的发送速率与接收方应用程序的读取速率相匹配。
拥塞控制:TCP发送方也可能因为IP网络的拥塞而被遏制;
3.接受窗口
发送方维护一个称为接收窗口(receive window)的变量来提供流量控制。通俗地说,接收窗口用于给发送方一个指示一一该接收方还有多少可用的缓存空间,因为TCP是全双工通信,在连接两端的发送方都各自维护一个接收窗口。
• LastByteRead:主机B上的应用进程从缓存读出的数据流的最后一个字节的编号。
• LastByteRcvd:从网络中到达的并且已放入主机B接收缓存中的数据流的最后一个字节的编号。’
由于TCP不允许已分配的缓存溢岀,下式必须成立:
LastByteRcvd - LastByteRead RcvBuffer
接收窗口用rwnd表示,根据缓存可用空间的数量来设置:
rwnd = RcvBuffer - [ LastByteRcvd - LastByteRead ]
主机B的接收缓存已经存满,使得rwnd = 0 在将rwnd = 0通告给主机A之后,还要假设主机B没有任何数据要发给主机A。所以 TCP规范中要求:当主机B的接收窗口为0时,主机A继续发送只有一个字节数据的报文段。
五、TCP连接管理(三次握手,四次挥手)
•第一步:
客户端的TCP首先向服务器端的TCP发送一个特殊的TCP报文段。该报
文段中不包含应用层数据。但是在报文段的首部(参见图3-29)中的一个标志位
(即SYN比特)被置为1。因此,这个特殊报文段被称为SYN报文段。另外,客
户会随机地选择一个初始序号(client_isn),并将此编号放置于该起始的TCP SYN
报文段的序号字段中。该报文段会被封装在一个IP数据报中,并发送给服务器。
为了避免某些安全性攻击
•第二步:
一旦包含TCP SYN报文段的IP数据报到达服务器主机(假定它的确到达
T!),服务器会从该数据报中提取出TCP SYN报文段,为该TCP连接分配TCP缓
存和变量,并向该客户TCP发送允许连接的报文段。这个允许连接的报文段也不包含应用层数据。
但是,在报文段的首部却包含3个重要的信息。首先,SYN比特被置为1。其次,该TCP报文段
首部的确认号字段被置为client _ isn + I 。最后,服务器选择自己的初始序号
,并将其放置到TCP报文段首部的序号字段中。这个允许连接的报文
段实际上表明了:
“我收到了你发起建立连接的SYN分组,该分组带有初始序号
clientjsno我同意建立该连接。我自己的初始序号是serverjsnon该允许连接的报文段被称为SYNACK报文段。
•第三步:
在收到SYNACK报文段后,客户也要给该连接分配缓存和变量。客户主
机则向服务器发送另外一个报文段;这最后一个报文段对服务器的允许连接的报
文段进行了确认(该客户通过将值serverjsn + 1放置到TCP报文段首部的确认字
段中来完成此项工作)。因为连接已经建立了,所以该SYN比特被置为0。该三次
握手的第三个阶段可以在报文段负载中携带客户到服务器的数据。
•第一步:
客户应用进程发出一个关闭连接命令。这会引起客户TCP向服务器
进程发送一个特殊的TCP报文段。这个特殊的报文段让其首部中的
一个标志位即FIN比特(参见图3-29)被设置为1
•第二步:
当服务器接收到该报文段后,就向发送
方回送一个确认报文段。
•第三步:
然后,服务器发
送它自己的终止报文段,其FIN比特被置
为1。
•第四步:
最后,该客户对这个服务器的终止
报文段进行确认。此时,在两台主机上用
于该连接的所有资源都被释放了。
六、TCP拥塞控制
1.TCP必须使用端到端拥塞控制而不是使网络辅助的拥塞控制,因为IP层不
向端系统提供显式的网络拥塞反馈。
TCP连接的每一端都是由
一个接收缓存、一个发送缓存和几个变量(LastByteRead、 rwnd等)组成。运行在发送方的TCP拥塞控制机制跟踪一个额外的变量,即拥塞窗口(congestion window) .拥塞窗口表示为cwnd,它对一个TCP发送方能向网络中发送流量的速率进行了限制。特别是,在一个发送方中未被确认的数据量不会超过cwnd与nvnd中的最小值,即
LastByteSent 一 LastByteAcked <= min { cwnd, rwnd }
3.为TCP使用确认来触发(或计时)增大它的拥塞窗口长度,TCP被说成是自计时(self-clocking)的。
4.注意到网络中没有明确的拥塞状态信令,即ACK和丢包事件充当了隐式信号,并且每个TCP发送方根据异步于其他TCP发送方的本地信息而行动。
TCP拥塞控制算法(TCP congestion control algorithm)
1.慢启动
因此,在慢启动(slow-start)状态,cwnd的值以1个MSS开始并且每当
传输的报文段首次被确认就增加1个MSS。在图所示的例子中,TCP向网络发送
第一个报文段并等待一个确认。当该确认到达时,TCP发送方将拥塞窗口增加一个
MSS,并发送出两个最大长度的报文段。这两个报文段被确认,则发送方对每个确认报
文段将拥塞窗口增加一个MSS,使得拥塞窗口变为4个MSS,并这样下去。这一过程每
过一个RTT,发送速率就翻番。因此,TCP发送速率起始慢,但在慢启动阶段以指数增长。
2.拥塞避免
1.当出现丢包事件时进入拥塞避免状态,一旦进入拥塞避免状态,cwnd的值大约是上次遇到拥塞时的值的一半,即距离拥塞可能并不遥远!
2.TCP无法每过一个RTT再将cwnd的值翻番,而是采用了一种较为
保守的方法,每个RTT只将cwnd的值增加一个MSS [RFC 5681]。
3.因此TCP对这种丢包事件的行为,相比于超时指示的丢包,
应当不那么剧烈:TCP将cwnd的值减半(为使测量结果更好,计及已收到的3个冗余的
ACK要加上3个MSS),并且当收到3个冗余的ACK,将ssthresh的值记录为cwnd的值的
一半。接下来进入快速恢复状态。
3.快速恢复
在快速恢复中,对于引起TCP进入快速恢复状态的缺失报文段,对收到的每个冗余的
ACK, cwnd的值增加一个MSS。最终,当对丢失报文段的一个ACK到达时,TCP在降低
cwnd后进入拥塞避免状态。如果出现超时事件,快速恢复在执行如同在慢启动和拥塞避
免中相同的动作后,迁移到慢启动状态:当丢包事件出现时,cwnd的值被设置为1个
MSS,并且ssthreshh ("慢启动阈值”的速记)设置为cwnd/2 ,即当检测到拥塞时将sst的值设置为 cwnd值的一半。
收到第3个重复的ACK时,将ssthresh设置为当前cwnd的一半。
设置cwnd=ssthresh+3
重传丢失的报文段
每收到一个重复的ACK,cwnd+1
确认新数据的ACK到达时,设置cwnd=ssthresh。
思考:1) 为何第二步操作的cwnd需要+3
2)为何第4步每次收到重复的ACK,cwnd会加一
3)为何新数据的ACK到达时,设置cwnd=ssthresh
理解:收到3个重复的ACK意味着网络很有可能没有阻塞。比如有报文段1,2,3,4 重复收到报文段1的序号,
那么意味着实际上2,3,4都是到达了对方,如果此时只将cwnd减半,那么很有可能未确认的报文数量是要大于或接近cwnd的,
此时数据几乎不发送,而当新的ACK达到时,滑动窗口右滑,又可能造成大量的数据同时发送出去。
既然有3个重复的ACK,那么至少有3个包是离开了网络的,我们就可以透支给cwnd+3,以后每当收到一个重复的ACK,
就意味着一个包离开网络,我们就可以给cwnd+1。而当新的ack到达时,如果不把cwnd还原,
很有可能会发送大量的数据出去,所以最好的办法是将透支的大小都给还回来
一种称为TCPTahoe的TCP早期版本,不管是发生超时指示的丢包事件,还是发生3个冗余ACK指示的丢包事件,都无条件地将其拥塞窗口减至1个MSS,并进入慢启动阶段。TCP的较新版本TCP Reno,则综合了快速恢复
图示了 Reno版TCP与Tahoe版TCP的拥塞控制窗口的演化情况。在该图中,阈值初始等于8个MSS。在前8个传输回合,Tahoe和Reno采取了相同的动作。拥塞窗口在慢启动阶段以指数速度快速爬升,在第4轮传输时到达了阈值。然后拥塞窗口以线性速度爬升,直到在第8轮传输后出现3个冗余ACK。注意到当该丢包事件发生时,拥塞窗口值为12xMSS。于是ssthresh的值被设置为0. 5 x cwnd =6 XMSS。在TCP Reno下,拥塞窗口被设置为cwnd = 9MSS,然后线性地增长。在TCP Tahoe下,拥塞窗口被设置为1个MSS,然后呈指数增长,直至到达ssthresh值为止,在这个点它开始线性增长。
4.
在深入了解慢启动、拥塞避免和快速恢复的细节后,现在有必要退回来回顾一下全
局。忽略一条连接开始时初始的慢启动阶段,假定丢包由3个冗余的ACK而不是超时指
示,TCP的拥塞控制是:每个RTT内cwnd线性(加性)增加1MSS,然后出现3个冗余
ACK事件时cwncl减半(乘性减)。因此,TCP拥塞控制常常被称为加性增、乘性减
(Additive- Increase, Multiplicative- Decrease,在图3 53中所示的“锯齿”行为,这也
很好地图示了我们前面TCP检测带宽时的直觉,即TCP线性地增加它的拥塞窗
口长度(因此增加其传输速率),直到出现3个冗余ACK事件。然后以2个因子
来减少它的拥塞窗口长度,然后又开始了线性增长,探测是否还有另外的可用带宽。