传输层负责建立端到端的连接。负责数据在端到端之间的传输。
传输层通过端口号来区分上下层服务。
TCP是面向字节流服务。
UDP是面向数据报服务。
TCP协议(Transmission Control Protocol),全称"传输控制协议"。是一种面向连接的,可靠的,基于字节流的传输层通信协议,是建立在网络层之上的端到端的协议。
特点:
两个应用程序通过TCP连接交换8bit字节构成的字节流。TCP不在字节流中插入记录标识符。将这称为字节流服务。如果一方的应用程序先传10字节,又传20字节,再传50字节,连接的另一方将无法了解发送方每次发送了多少字节。接收方可以分4次接收这80个字节,每次接收20字节。一端将字节流放到TCP连接上,同样的字节流将出现在TCP连接的另一端。
另外,TCP对字节流的内容不作任何解释。TCP不知道传输的数据字节流是二进制数据,还是ASCI字符、EBCDIC字符或者其他类型数据。对字节流的解释由TCP连接双方的应用层解释。
大多数知名端口为奇数的由来
如果仔细检查这些标准的简单服务以及其他标准的TCP/IP服务(如Telnet、FTP、SMTP等)的端口号时,发现它们都是奇数。这是因为这些端口号都是从NCP端口号派生出来的(NCP即网络控制协议,是ARPANET的运输层协议,是TCP的前身)。NCP是单工的,不是全双工的,因此每个应用程序需要两个连接,需预留一对奇数和偶数端口号,一个端口号发另一个端口接收。当TCP和UDP成为标准的运输层协议时,每个应用程序只需要一个端口号,因此就使用了NCP中的奇数。
字段解析:
说明:
TCP数据被封装在IP数据报中。
说明:
在有些书中,将它看作可"协商”选项。它并不是任何条件下都可协商。当建立一个连接时,每方都有用于通告它期望接收的MSS选项(MSS选项只能出现在SYN报文段中)。如果一方不接收来自另一方的MSS值,则MSS就定为默认值536字节。
一般说来,如果没有分段发生,MSS还是越大越好。报文段越大,允许每个报文段传送的数据就越多,相对IP和TCP首部有更高的网络利用率。当TCP发送一个SYN时,或者是因为一个本地应用进程想发起一个连接,或者是因为另一端的主机收到了一个连接请求,它能将MSS值设置为外出接口上的MTU长度减去固定的 IP 首部和 TCP 首部长度。对于一个以太网,MSS值可达1460字节。使用IEEE802.3的封装,它的MSS可达1452字节。
如果目的IP地址为"非本地的(nonlocal)”,MSS通常的默认值为536。而区分地址是本地还是非本地是简单的,如果目的IP地址的网络号与子网号和我们的相同,则是本地的;如果目的IP地址的网络号与我们的完全不同,则是非本地的;如果目的IP地址的网络号与我们的相同而子网号与我们的不同,则可能是本地的,也可能是非本地的。大多数TCP实现版都提供了一个配置选项让系统管理员说明不同的子网是属于本地还是非本地。这个选项的设置将确定MSS可以选择尽可能的大(达到外出接口的MTU长度)或是默认值536。
TCP包头选项字段解码。
Wireshark抓包查看
说明:Shift Count(窗口放大因子)可以发送更多的数据,窗口大小和窗口放大因子相乘。
三次握手没有传输数据,三次握手成功后才开始传输数据。
说明:
Wireshark抓包查看
TCP三次握手的SYN数据包
TCP三次握手的SYN,ACK数据包
TCP三次握手的ACK数据包
被客户端方向RST:可能是存在网络扫描的行为,只是试探某些端口是否开放。
被服务器方向RST:可能是存在网络扫描的行为,只是试探某些端口是否开放。如果服务器的端口没有打开,那么服务器就会回复ACK,RST 数据段不进行建立连接。
OmniPeek抓包查看
首先在本机进行Telnet虚拟机中一个未开放的端口
虚拟机中抓包查看
TCP半连接:客户端在发出连接请求后不在发送任何数据。
半连接扫描通过不完全建立TCP连接的方式其扫描过程如下:
客户端在和服务器建立连接的时候,服务器没有响应。有可能是对方服务器没有开启,或者服务器有防火墙将我们的数据包丢弃了。
详细说明
说明:
注意:在建立三次握手的时候是+1,而在后续发送数据的时候是发送多少个字节加多少个字节。
应用层将数据准备好后,然后给到TCP协议,TCP协议负责分段发送。通过发送缓存然后将数据分段,发送到对方服务器的接受缓存中。
TCP 累计确认
累计确认:对最后一个Seq数据包的确认,意味着前面所有的数据包也已经确认了。
说明:五个包的编号是6、7、8、9、10号包,两个ACK包的编号为11、12号包。其中11号包是对6号包+7号包部分内容的确认,12号包是对7、8、9、10号包的确认。这几个包从Server的接收发送顺序应该是6、7、11、8、9、10、12,但是由于数据包捕获位置和网络时延原因,所以从Client位置看到的顺序就变成了6、7、8、9、10、11、12。
说明:
TCP滑动窗口技术通过改变窗口大小实现流量控制,如图所示,服务器以ACK 3073响应,调整窗口大小3072,主机A改变其发送数据包大小。
说明:
Wireshark抓包查看
TCP四次挥手的FIN,ACK的数据包
TCP四次挥手的ACK的数据包
TCP四次挥手的FIN,ACK的数据包。
TCP四次挥手的ACK的数据包
OminPeek抓包查看
TCP四次挥手的FIN,ACK的数据包。
TCP四次挥手的ACK的数据包。
TCP四次挥手的FIN,ACK的数据包。
TCP四次挥手的ACK的数据包。
TCP状态如下:
cmd输入如下命令:
netstat -ano
TIME WAIT状态也称为2MSL等待状态。每个具体TCP实现必须选择一个报文段最大生存时间MSL(Maximum Segment Lifetime)。它是任何报文段被丢弃前在网络内的最长时间。
在FIN_WAIT_2状态是已经发出了FIN,并且另一端也已对它进行确认。除非在实行半关闭,否则将等待另一端的应用层意识到它已收到一个文件结束符说明,并向我们发一个 FIN 来关闭另一方向的连接。只有当另一端的进程完成这个关闭,我们这端才会从FIN_WAIT_2状态进入TIME WAIT状态。这意味着我们这端可能永远保持这个状态。另一端也将处于CLOSE WAIT状态,并一直保持这个状态直到应用层决定进行关闭。
TCP为应用层提供全双工服务。
同时连接
同时关闭
如果按照分组数量计算,约有一半的TCP报文段包含成块数据(如FTP、电子邮件等)另一半则包含交互数据(如Telnet 和 Rlogin [也是一种远程登录协议] )。如果按字节计算,则成块数据与交互数据的比例约为90%和10%。TCP需要同时处理这两类数据,但使用的处理算法则有所不同。
本章将以Rlogin应用为例来观察交互数据的传输过程。
在TCP交互数据流中,数据被分割成称为数据段(segment)的小块,这些数据段会通过网络从发送方传输到接收方。每个数据段都包含了序列号(sequence number)来标识数据在整个数据流中的顺序,并且有确认号(acknowledgment number)用于确认已经接收到的数据。
对于交互式输入,客户端可以向服务器发送用户输入的命令或数据,然后服务器接收并处理这些数据,并将结果返回给客户端。客户端再次接收服务器返回的结果,并根据需要继续发送更多的命令或数据。
通常每一个交互按键都会产生一个数据分组。客户端发一个字节,服务器确认一个字节。
这样会产生4个报文段:
经受时延的确认(Delayed Acknowledgment)是指 TCP 协议中一种优化技术,它可以将多个 ACK 确认报文合并成一个,从而减少网络上的报文交换次数,提高网络性能和吞吐量。
TCP 协议中规定,接收方必须对每个成功接收的数据包发送一个 ACK 确认报文。但是,如果接收方每次都立即返回 ACK 确认报文,那么网络上的报文交换次数会明显增加,导致网络性能下降。因此在实际应用中,通常会使用经受时延的确认技术来优化 TCP 连接的性能。
Nagle 算法的核心思想是将多个较小的数据块合并成一个较大的数据块,并在延迟一定时间后才发送。具体当发送方准备发送数据时,如果之前的数据还没有收到确认,那么当前的数据将被缓存起来,等待之前的数据得到确认后再一起发送。这样做的目的是避免发送过多的小数据包,从而减少网络上的报文交换次数。所以在客户端的窗口大小是小于4096的,而服务器端的窗口大小通常是全窗口大小8192。
该算法要求一个TCP连接上最多只能有一个未被确认的小分组,在该分组的确认到达之前不能发送其他的小分组。相反,TCP收集这些小分组,并在确认到来时以一个分组(包含多个小分组的数据)的方式发出去。该算法的优越之处在于它是自适应的:确认到达得越快,数据也就发送得越快。默认情况下,Nagle算法都是启用的。
注意到从左到右待发数据的长度是不同的分别为:1、1、2、1、2、2、3、1和3个字节。这是因为客户只有收到前一个数据的确认后才发送已经收集的数据。通过使用Nagle算法,为发送16个字节的数据客户只需要使用9个报文段,而不再是16个。
窗口大小
有时我们也需要关闭Nagle算法。比如终端的功能键(F1键等)通常可以产生多个字符序列。当开启Nagle算法时,TCP很可能会发送序列中的第一个字符,然后缓存其他字符并等待对该字符的确认。但当服务器接收到该字符后,它并不发送确认,而是继续等待接收序列中的其他字符。这就会经常触发服务器的经受时延的确认算法。对交互用户而言,这将产生明显的时延。
TFTP使用了停止等待协议。数据发送方在发送下一个数据块之前需要等待接收对已发送数据的确认。
TCP使用滑动窗口协议,该协议允许发送方在停止并等待确认前可以连续发送多个分组。由于发送方不必每发一个分组就停下来等待确认,因此该协议可以加速数据的传输。
通常使用的 “隔一个报文段确认” 的策略。在线路上看到的分组顺序依赖于许多无法控制的因素:发送方TCP的实现、接收方TCP的实现、接收进程读取数据(依赖于操作系统的调度和网络的动态性(如以太网的冲突和退避等)。对这两个TCP而言,没有一种单一的、正确的方法来交换给定数量的数据。
说明:
下图是从一个快的发送方发送8092字节数据到一个慢的接收方产生的数据流:
下图用可视化的方法显示了上面观察到的滑动窗口协议:
待发送的数据字节从1至11标号,这些数据分为如下四部分:
已经发送并被确认(1~3)。
已经发送但未被确认(4~6)。
能够发送(7~9)。
不能够发送(10~11)。
接收方通告的窗口被称为提供的窗口,它覆盖49字节的区域(窗口大小为6)。发送方计算它的可用窗口(79),该窗口表明多少数据可以立即被发送。使用三个术语来描述窗口左右边沿的运动:
如果左边沿到达右边沿,则称其为一个零窗口,此时发送方不能够发送任何数据。
下图说明了第一个图中所示的数据传输过程中滑动窗口协议的动态性:
以该图为例可以总结如下几点:
由接收方提供的窗口的大小和接收缓冲区大小相关,这会影响TCP的性能。缓冲区(接收和发送)大小可以由接收进程设置。
在4.3 BSD中,发送方和接收方的默认缓冲区大小为4096个字节。4.4 BSD中则使用更大的数值,如8192或16384。一份研究表明:对以太网而言,默认的4096字节并不是最理想的缓冲区大小,将大小增加到16384个字节可以增加约40%左右的吞吐量。
BSD(Berkeley Software Distribution)是一个基于Unix操作系统的开源软件项目。
下图是一个接收方使用6144字节大小的缓冲区的例子:
注意的是14、15和16这三个连续的窗口更新报文段。(也就是表示放大窗口,告诉客户端这里有多余的空间,可以进行发送数据。)
在所有的例子中,发送方一开始便向网络发送多个报文段,直至达到接收方通告的窗口大小为止。当发送方和接收方处于同一个局域网时,这种方式是可以的。但是如果在发送方和接收方之间存在多个路由器和速率较慢的链路时,就有可能出现一些问题。
为解决这些问题,TCP使用一种被称为 “慢启动(slow start)” 的算法。该算法通过观察到新分组进入网络的速率应该与另一端返回确认的速率相同而进行工作。
慢启动为发送方的TCP增加了另一个窗口:拥塞窗口(congestion window),记为cwnd。当与另一个网络的主机建立TCP连接时,拥塞窗口被初始化为1个报文段(即另一端通告的报文段大小)。每收到一个ACK,拥塞窗口就增加一个报文段(cwnd以字节为单位,但是慢启动以报文段大小为单位进行增加)。发送方取拥塞窗口与通告窗口中的最小值作为发送上限。拥塞窗口是发送方使用的流量控制,而通告窗口则是接收方使用的流量控制。
发送方开始时发送一个报文段,然后等待ACK。当收到该ACK时拥塞窗口从1增加为2,即可以发送两个报文段。当收到这两个报文段的ACK时,拥塞窗口就增加为4。这是一种指数增加的关系。
示例:
对发送端:
第一次,发 1 个MSS。
等待这1个MSS的ACK,并收到 1 个ACK。由于原来可以发1个MSS,现在又增加了一个。因此,第二次可以发2个MSS。
第二次,发 2 个MSS。
等待这2个MSS的ACK, 并收到2个MSS的ACK。由于原来有2个MSS,现在收到2个ACK,又可以增加2个MSS。因此,下一次可以发送2+2=4个MSS。
第三次,发送4个MSS。
…
对于发送端:
注意:这中间拥塞窗口cwnd不是跳跃增长的(如,1,2,4,8,16),而是每收到一个ACK之后,都会增长1,而不是等所有的ACK都到了之后,一次性跳跃增长。(见上图中的cwnd值:1,2,3,4,5)。而且,只要能发送数据(当前已发送的数据量小于对端的通告窗口和cwnd中的小者),就会发送数据。实际上,并不存在所谓的“第二次必须一次性发2个MSS”、“第三次一次性发4个MSS”的说法。整个发送过程和接收ACK的过程,都是连续的,而非集中和跳跃的。
在时间0,发送方发送了一个报文段。由于发送方处于慢启动中(其拥塞窗口为1个报文段),因此在继续发送以前它必须等待该数据段的确认。在时间1, 2和3,报文段从左向右移动一个时间单元。在时间4接收方读取这个报文段并产生确认。经过时间5、6和7,ACK移动到左边的发送方。 于是我们有了一个8个时间单元的往返时间(Round-TripTime,RTT)。
当发送方收到ACK后,在时间8和9发送两个报文段(标记为2和3)。此时它的拥塞窗口为2个报文段。这两个报文段向右传送到接收方,在时间12和13接收方产生两个ACK。
2个ACK的到达使得拥塞窗口从2个报文段增加为4个,而这4个报文段在时间1619时被发送。第1个ACK在时间23到达。4个ACK的到达使得拥塞窗口从4个报文段增加为8个,并在时间2431发送8个报文段。
在时间31及其后续时间,发送方和接收方之间的管道被填满。此时不论拥塞窗口和通告窗口是多少,它都不能再容纳更多的数据。每当接收方在某一个时间单位从网络上移去一个报文段,发送方就再发送一个报文段到网络上。但是不管有多少报文段填充了这个管道,返回路径上总是具有相同数目的ACK。这就是连接的理想稳定状态。
现在来回答窗口应该设置为多大的问题。 在例子中,作为最大的吞吐量,发送方在任何时候有8个已发送的报文段未被确认。接收方的通告窗口必须不小于这个数目,因为通告窗口限制了发送方能够发送的段的数目。
可以计算通道的容量为:带宽 * 往返时间
一般称之为带宽时延乘积。
当数据到达一个大的管道(如一个快速局域网)并向一个较小的管道(如一个较慢的广域网)发送时便会发生拥塞。当多个输入流到达一个路由器,而路由器的输出流小于这些输入流的总和时也会发生拥塞。
下图显示了一个典型的大管道向小管道发送报文的情况:
在该图中,已经标记路由器 R1 为“瓶颈”,因为它是拥塞发生的地方。它从左侧速率较高的局域网接收数据并向右侧速率较低的广域网发送。 当路由器R2将所接收到的分组发送到右侧的局域网时,这些分组之间维持与其左侧广域网上同样的间隔,尽管局域网具有更高的带宽。类似地,返回的确认之间的间隔也与其在路径中最慢的链路上的间隔一致。
TCP提供可靠的传输层。它使用的方法之一就是确认从另一端收到的数据。但数据和确认都有可能会丢失。TCP通过在发送时设置一个定时器来解决这种问题。如果当定时器溢出出时还没有收到确认,它就重传该数据。对任何实现而言,关键之处就在于超时和重传的策略,即怎样决定超时间隔和如何确定重传的频率。
对每个连接,TCP管理4个不同的定时器。
示例
从上图中观察连续重传之间的时间差(称为超时重传时间,RTO),它们取整后分别为1、3、6、12、24、48和多个64秒。这个倍乘关系被称为 “指数退避(exponential backoff)”。在多个64秒超时之后,TCP发送复位报文段。注意:实际上TCP设置的第一个超时时间是1.5秒,而不是1秒。
目前TCP的实现,重传超时时间都是以RTT的幂次倍来实现的,即RTO = RTT * 2 ^n, n表示第几次重传,即超时时间是指数递增,而非线性递增,RTO的最大值为超过64秒。
需要注意的是,RTT测量采样时,不计入重传的报文段。这是因为,一个报文段一旦重传,无法区分收到的该报文段的ACK对应于哪次发送的报文段。
重传多义性问题
在一个分组(数据包)重传时会产生这样一个问题:假定一个分组(数据包)被发送。当超时发生时,RTO正进行指数退避,分组以更长的RTO进行重传,然后收到一个确认。那么这个ACK是针对第一个数据包的还是针对第二个数据包呢?
Karn算法
当一个超时和重传发生时,在重传数据的确认最后到达之前,不能更新RTT估计器。
看到TCP在5秒的时间内获取到了3次RTT值(重传超时时间值),分别是1.061秒、0.808秒和1.015秒。
大多数的TCP实现在任何时候对每个连接仅测量一次RTT值。
下图显示了本例中实际RTT与时钟滴答计数之间的关系:
虚线是500ms定时器溢出的时刻。以1.061秒的RTT为例:TCP发送报文段1时的时刻为0,此时开始对报文段计时。在0.03秒的时刻,500ms定时器第一次溢出,计数器的值为1。在0.53秒的时刻,定时器第二次溢出,计数器值为2。在1.03秒的时候定时器第三次溢出,计数器的值为3。在1.061秒的时刻,收到包含报文段1的数据起始序号的ACK,因此不再计数,这个报文段的RTT为3个滴答,也就是1.5秒。
对每个连接而言,除了这个滴答计数器,报文段中数据的起始序号也被记录下来。当收到一个包含这个序号的确认后,该定时器就被关闭。如果ACK到达时数据没有被重传,则被平滑的RTT和被平滑的均值偏差将基于这个新测量进行更新。
下图是例子中发生拥塞时的一个数据传输过程:
说明:
拥塞避免算法是一种处理丢失分组的方法。
该算法假定由于分组受到损坏引起的丢失是非常少的(远小于1%),因此分组丢失就意味着在源主机和目的主机之间的某处网络上发生拥塞。
有两种分组丢失的指示:发生超时和接收到重复的确认。如果使用超时作为拥塞指示,则需要使用一个好的RTT算法。
拥塞避免算法和慢启动算法是两个目的不同、独立的算法。当拥塞发生时,降低分组进入网络的传输速率,于是可以调用慢启动来作到这一点。在实际中这两个算法通常在一起实现。
拥塞避免算法和慢启动算法需要对每个连接维持两个变量:一个拥塞窗口cwnd和一个慢启动阀值ssthresh。这样得到的算法的工作过程如下:
对一个给定的连接,初始化cwnd(拥塞窗口)为1个报文段,ssthresh(慢启动阀值)为65535个字节。
TCP输出例程的输出不能超过cwnd和接收方通告窗口的大小。拥塞避免是发送方使用的流量控制,而通告窗口则是接收方进行的流量控制。前者是发送方感受到的网络拥塞的估计,而后者则与接收方在该连接上的可用缓存大小有关。
当拥塞发生时(超时或收到重复确认),ssthresh被设置为当前窗口大小(cwnd和接收方通告窗口大小的最小值,但最少为2个报文段)的一半。此外,如果是超时引起了拥塞,则cwnd被设置为1个报文段(这就是慢启动)。
注意:拥塞发生的两种情况:
- 超时:是发送端发送了数据后,启动重传定时器,在规定时间内未收到ACK,于是发生了超时。
- 重复ACK:是指在接收方收到乱序报文时,所发出的一类TCP报文。重复ACK,是因为数据未按序到达接收端造成的,是由于接收端触发的,是对到目前为止收到的连续数据段的确认,重复ACK,是接收端触发并通知到发送端;例如,现在接收端已经收到0-35个字节,已给客户端回复ACK=36,第36以后的字节未收到;但这时收到了40-43字节,但由于第36-39字节并未收到,于是回复给发送端的ACK仍是ACK=36,这对发送端来说,ACK=36 即是一个重复ACK;这时,又收到了44-47字节,仍是因为未收到36~39字节,所以必须回复给发送端ACK=36,这对发送端来说,ACK=36,又是一个重复确认的ACK。
当新的数据被对方确认时,就增加cwnd,但增加的方法依赖于我们是否正在进行慢启动或拥塞避免。如果cwnd小于或等于ssthresh,则正在进行慢启动,否则正在进行拥塞避免。慢启动一直持续到我们回到当拥塞发生时所处位置一半的时候才停止,然后转为执行拥塞避免。
慢启动算法及拥塞避免算法的区别
下图是慢启动和拥塞避免的一个可是化描述:
说明:以段为单位来显示 cwnd和 ssthresh(慢启动阀值),但它们实际上都是以字节为单位进行维护的。 在该图中,假定当cwnd为32个报文段时就会发生拥塞。于是设置ssthresh为16个报文段,而cwnd为1个报文段。在时刻0发送了一个报文段,并假定在时刻1接收到它的ACK,此时cwnd增加为2。接着发送了2个报文段,并假定在时刻2接收到它们的ACK,于是cwnd增加为4(对每个ACK增加1次)。这种指数增加算法一直进行到在时刻3和4之间收到8个ACK后cwnd等于ssthresh时才停止,从该时刻起,cwnd以线性方式增加,在每个往返时间内最多增加1个报文段。
在该例中,建立连接时,第一个SYN超时。
假设MMS为256,发送端(发起连接端)初始设置cwnd为256(即1个报文段),ssthresh初始值设为65535,然后发出第一个SYN包。但该SYN包超时了,在定时器提示超时后,设置ssthresh为cwnd的一半,但ssthresh最小也必须是512(即2个报文段),于是设置ssthresh为512。
接着发出第2个SYN包。然后收到了接收端的SYN+ACK 包,这时自己这端的cwnd仍为256,仍处理慢启动,于是发出数据包,在发出数据包后接着设置自己这一端的ssthresh为2个报文段。(注意,这里在实现上是:接收到对端发来的数据包,参考自己当前的cwnd值,发出数据包,然后再设置自己的cwnd值------先发数据包后改cwnd)。
快速重传算法:如果一连串收到3个或3个以上的重复ACK,就非常可能是一个报文段丢失。于是就重传丢失的数据报文段,而无需等待超时定时器溢出。
快速恢复算法:快速重传后执行的不是慢启动算法,而是拥塞避免算法。
在拥塞举例中:在这种情况下没有执行慢启动的原因是由于收到重复的ACK不仅仅告诉我们一个分组丢失了。由于接收方只有在收到另一个报文段时才会产生重复的ACK,而该报文段已经离开了网络并进入了接收方的缓存。也就是说,在收发两端之间仍然有流动的数据,而我们不想执行慢启动来突然减少数据流。(降低速率)
示例:
说明:当最初的2个重复的ACK(报文段60和61)到达时它们被计数,而cwnd保持不变(也就下图中处理重传之前的平坦的一段)。然而,当第3个重复的ACK到达时,ssthresh被置为cwnd的一半(四舍五入到报文段大小的下一个倍数)而cwnd被置为ssthresh加上所收到的重复的ACK数乘以报文段大小(也即1024加上3倍的256),然后发送重传数据。又有5个重复的ACK到达(报文段64~66,68和70),每次cwnd增加1个报文段长度。最后一个新的ACK(报文段72段)到达时,cwnd被置为ssthresh(1024)并进入正常的拥塞避免过程。由于cwnd小于等于ssthresh(现在相等),因此报文段的大小增加到cwnd,取值为1280。
TCP能够遇到的最常见的ICMP差错就是源站抑制、主机不可达和网络不可达。对这些错误的处理是:
注意:对于一条TCP连接,如果一个中间设备给发送方回复了远端主机不可达,那么发送方会忽略掉该报文,认为网络只是暂时的不可达,而不是将TCP连接关闭。
当TCP超时并重传时,它不一定要重传同样的报文段。相反,TCP允许进行重新分组而发送一个较大的报文段,这将有助于提高性能(当然,这个较大的报文段不能够超过接收方声明的MSS(最大报文长度))。
ACK的传输并不可靠,也就是说,TCP不对ACK报文段进行确认,TCP只确认那些包含有数据的ACK报文段。如上图中的报文段9丢失怎么办?
问题和解决方法
发送端在接收到接收端通告窗口大小为0的报文后,会启动一个定时器,即坚持定时器,在一定时间内未收到接收端通告窗口大小的报文,发送方将主动发送一个查询报文,向接收端查询通告窗口大小,接收端收到该报文后,回复窗口大小的报文(如第9个报文)。
示例
特点
糊涂窗口综合症,SWS(Silly Window Syndrome)糊涂窗口综合症是指,接收方发送的通知窗口很小,导致发送方每次只能发送较小的报文段(远不到一个MSS,比如一次发送1Byte的数据),这样会导致整个TCP的效率非常低(因为TCP头,IP头等的封装,使得有效数据所占的比率非常低)。
该现象可发生在两端中的任何一端:接收方可以通告一个小的窗口(而不是一直等到有大的窗口时才通告),而发送方也可以发送少量的数据(而不是等待其他的数据以便发送一个大的报文段)。可以在任何一端采取措施避免出现糊涂窗口综合症的现象。
糊涂窗口综合症解决
接收方不通告小窗口。 通常的算法是接收方不通告一个比当前窗口大的窗口 (可以为0),除非窗口可以增加一个报文段大小(也就是将要接收的 MSS)或者可以增加接收方缓存空间的一半,不论实际有多少。
发送方避免出现糊涂窗口综合症的措施是只有以下条件之一满足时才发送数据:
示例:
引言
如果TCP连接的双方都没有向对方发送数据,则在两个TCP模块之间不交换任何信息。
这意味着我们可以启动一个客户与服务器建立一个连接,然后离去数小时、数天、数个星期或者数月,而连接依然保持。中间路由器可以崩溃和重启,电话线可以被挂断再连通,但是只要两端的主机没有被重启,则连接依然保持建立。
保活并不是TCP规范中的一部分。 Host Requirements RFC提供了3个不使用保活定时器的理由:
然而,许多实现提供了保活定时器。保活定时器是一个有争论的功能。许多人认为如果需要,这个功能不应该在 TCP中提供,而应该由应用程序来完成。
工作细节
如果一个给定的连接在两个小时之内没有任何动作,则服务器就向客户发送一个探查报文段。客户主机必须处于以下4个状态之一。
示例
远端主机关机或者崩溃
远端主机崩溃并重启
远端主机不可达