一、传输层基本介绍#
1. 基本职能#
传输层位于计算机网络体系五层结构中的第四层,它的职能是:为运行在不同主机上的进程提供直接的通信服务。
和网络层提供服务的范围差异如下图所示:
即网络层提供了主机到主机间的通信(如果是IP协议的话还是不可靠的通信),而传输层提供的是主机中的进程与进程之间的通信(TCP协议提供可靠传输,UDP协议提供不可靠传输)。
更加直观的示意图如下所示:
2. 运输层端口号、复用分用的概念#
端口号#
为了使运行不同操作系统的计算机的应用进程之间能够进行网络通信,就必须使用统一的方法对TCP/IP体系的应用进程进行标识。
这里用于标识不同应用进程的东西就是端口号:
- 端口号使用16比特表示,取值范围0~65535;
- 端口号只具有本地意义,即端口号只是为了标识本计算机应用层中的各进程,在因特网中,不同计算机中的相同端口号是没有联系的。
端口号分类:
熟知端口号 | 登记端口号 | 短暂端口号 |
---|---|---|
熟知端口号: 0~1023, IANA把这些端口号指派给了TCP/IP体系中最重要的一些应用协议,例如: FTP使用21/20,HTTP使用80,DNS使用53。 | 1024~49151, 为没有熟知端口号的应用程序使用。使用这类端口号必须在IANA按照规定的手续登记,以防止重复。例如:Microsoft RDP微软远程桌面使用的端口是3389。 | 49152~65535, 留给客户进程选择暂时使用。当服务器进程收到客户进程的报文时,就知道了客户进程所使用的动态端口号。通信结束后,这个端口号可供其他客户进程以后使用。 |
发送方的复用和接收方的分用#
TCP/IP体系结构应用层常用协议所使用的运输层熟知端口号:
二、UDP与TCP对比#
UDP和TCP是计算机网络层的两个重要协议,如下图所示:
1. 是否连接#
UDP协议是无连接的,TCP协议是面向连接的,如下所示:
2. 单播、多播和广播#
3. 面向报文和面向字节流#
之所以TCP需要面向字节流,是因为TCP需要实现可靠传输、流量控制和拥塞控制,面向字节流是实现上述技术的基础。
4. 可靠性#
5. 两种协议的格式#
因为TCP需要实现的功能较为复杂,因此TCP报文段的首部要比UDP数据包的首部大得多。
6. 小结#
三、TCP的流量控制#
一般情况下,我们都是希望信息可以尽快传输,但如果是TCP连接一对一进行传输时,如果不进行流量控制,就会出现一方传输速度过快,导致另一方的接收信息的缓存满了,如果再继续发送信息只会导致这部分信息的丢失,然后需要进行信息重传等操作,进而不仅信息传输比原来要慢很多,还占用了很多额外的网络资源。
TCP协议采用了滑动窗口的机制来进行流量控制。
1. 流量控制的相关术语#
发送窗口和接收窗口
逻辑上,发送端有一个控制发送数据量的窗口,称为发送窗口,当发送端将发送窗口中的所有数据都发送出去后,还没有收到接收端的应答之前,都不能进行下一步的操作,例如移动窗口,发送新数据等等。
而接收窗口相当于接收端当前可以接受数据量的大小。
发送窗口的大小由接收端(接收窗口大小)来决定,这是保证流量控制,保证数据不溢出的关键。
2. 流量控制的流程#
- 发送端发送自己发送窗口中的全部或部分数据到接收端,例如(浅蓝色部分为已发送部分):
- 接收端接收到后发送相应数据,其中包括:对数据的累计确认和当前接收窗口的大小(rwnd)。
- 发送端接收到数据确认信息和rwnd后将①发送窗口前移,②调整窗口的大小,再将已确认的数据从发送缓存中删除,然后回到1继续进行
当前接收方发送的rwnd为0,即接受窗口的大小为0,所以发送窗口也被调控为0,因而发送方无法继续发送数据。
考虑当接收方又出现了一定的接收空间,然后将当前的rwnd发送到发送方,但此时这个报文段丢失了,那么发送端无法接收到rwnd的变化信息,因而又无法发送数据,此时就会造成双方都在等待对方相应的状况,即死锁,如下图所示:
其实这种情况下,即发送方接收到rwnd=0
的信息后会启动持续计时器,当这个阶段还未接收到接收端发来的信息时就会发送零窗口探测报文进行询问,以打破死锁局面:
疑问1:零窗口探测报文如果丢失了怎么处理?回答:零窗口探测报文也有超时重传计时器,保证可以不断发送探测报文
疑问2:如果接收窗口为0如何接收零窗口探测报文?回答:TCP规定这种特殊情况的报文接收端也必须有机制进行接收
例题:
3. 小结#
四、TCP的拥塞控制#
1. 简单介绍#
2. 拥塞控制算法#
包括四个子算法:
- 慢开始
- 拥塞避免
- 快重传
- 快恢复
3. 相关概念#
为了实现拥塞控制,发送方除了需要维护swnd(发送窗口)之外,还需要额外维护一个新的变量cwnd(拥塞窗口),它的值取决于拥塞程度,而且动态变化。
此时,swnd = min(rwnd, cwnd)
,即发送窗口的大小应为接收窗口和拥塞窗口的最小值,进而同时实现流量控制和拥塞控制。
cwnd的变化规律:
- 网络没有出现拥塞时,cwnd再增大一些
- 一旦出现拥塞,cwnd就要减小一些
判断网络是否发生拥塞的依据:
- 重传定时器超时
- 收到三个相同(重复)的 ACK
其实这两个依据的本质都是相同的,即发出的报文段丢失了。
最后,为了cwnd的增大变化更加合理,在特定的情况下采用特定的算法进行增大,发送端还需要维护一个慢开始门限变量ssthresh状态变量:
4. 拥塞控制的流程#
为了能够更简单地说明问题,假定如下条件:
4.1 慢开始#
连接成功后,假设发送端的拥塞窗口值被设置为1,此外还需要设置慢开始门限的初始值,这里以16为例。
慢开始算法:每次收到一个ACK,cwnd += 1。
具体案例如下所示:
因此在理想情况下运行慢开始算法时是每接收到一轮回复时cwnd *= 2
,我们在做题时往往是考虑这样的理想情况的。
拥塞窗口的大小随传输轮次的增大的变化如下图所示:
4.2 拥塞避免#
当cwnd达到ssthresh(慢开始门限)时,就会转而运行拥塞避免算法,即:每收到一个新的ACK,将CWND += MSS / CWND
在理想条件下,若每个ACK都没有延迟,则在每个RTT之后,cwnd的变化相当于cwnd += 1
。
当出现某报文段发生超时重传时,判断网络发生了拥塞,此时进行如下操作:
即步骤为:
- 执行慢开始算法
- cwnd达到ssthresh后执行拥塞避免算法
- 发生拥塞后更新:
ssthresh /= 2; cwnd = 1;
,然后转1继续运行
按照以上步骤执行直到信息发送完全为止。
因而总的流程如下图所示:
tips:
- “慢开始”是指一开始向网络注入的报文段少,并不是指拥塞窗口cwnd增长速度慢;
- “拥塞避免”并非指完全能够避免拥塞,而是指在拥塞避免阶段将拥塞窗口控制为按线性规律增长,使网络比较不容易出现拥塞;
慢开始和拥塞避免算法是1988年提出的TCP拥塞控制算法(TCP Tahoe版本)。
1990年又增加了两个新的拥塞控制算法(改进TCP的性能),这就是快重传和快恢复(TCP Reno版本)。
原因:有时,个别报文段会在网络中丢失,但实际上网络并未发生拥塞。这将导致发送方超时重传,并误认为网络发生了拥塞,进而发送方把拥塞窗口cwnd又设置为最小值1,并错误地启动慢开始算法,因而降低了传输效率。
4.3 快重传#
执行快重传算法的过程如下:
即使用这个算法可以减少发送端对网络拥塞的误判,使发送端更早地对丢失报文段进行重传,进而提高传输效率。
4.4 快恢复#
4.5 四种算法的综合使用#
下图的传输过程包括了四种算法的使用:
TCP Reno版本的拥塞控制如下:
5. 例题#
五、TCP超时重传时间RTO的计算#
1. 问题引入#
可见,RTO太小或太大都不合适,太小时会使网络符合增大,太大时会降低传输速率。
因此,一般我们会取一个比往返时间RTT稍大的值作为RTO的值,如下图所示:
2. RTO计算公式#
要计算RTO,首先我们需要先计算加权平均往返时间RTTS,计算公式为:
即新的RTTS由新的RTT样本和旧的RTTS同时决定,里面α的取值确定了两者的重要性,根据标准一般取值为1/8,即旧的RTTS权重较大:
而RTO的大小要略大于RTTS,其完整的计算公式如下:
RTTS的计算过程已经提到过了,即:
而RTTD的计算如下:
按道理我们已经可以计算每一传输轮次的RTTS了,但是实际上还是会出现问题,这里主要是RTT样本在发生重传时很可能出现偏差,如下图所示:
因此我们需要解决这种网络不可靠情况下的RTT测量问题,这里主要是使用Karn算法:
在计算RTTS的过程中,只要报文段进行了重传,就不采用该报文传输得到的RTT样本。(样本重传不采用)
但这样也会出现特殊情况无法更新RTO:报文段的时延突然增大了很多,并且之后很长一段时间都会保持这种时延。因此在原来得出的重传时间内,不会收到确认报文段。于是就重传报文段。但根据Karn算法,不考虑重传的报文段的往返时间样本。这样,超时重传时间就无法更新。这会导致报文段反复被重传。
因此我们使用修正后的Karn算法:报文段每重传一次,就把超时重传时间RTO增大一些。典型的做法是将新RTO的值取为旧RTO值的2倍。(RTO *= 2
)
以下是一个计算的例子:
总的计算方法如下图所示:
六、TCP滑动窗口和可靠传输#
1. 基于滑动窗口的发送过程#
TCP基于以字节为单位的滑动窗口来实现可靠传输。
滑动窗口示意图如下所示:
实现上维护3个指针来描述发送窗口的状态:
具体的报文段发送过程如下所示:
2. 注意点#
发送窗口大小#
对不按序到达数据的处理#
确认机制#
通信方式#
3. 例题#
例1:
例2:
七、TCP连接的建立#
TCP是面向连接的协议,它基于运输连接来传送TCP报文段。因此TCP运输连接的建立和释放是每一次面向连接的通信中必不可少的过程。
1. 连接建立需要解决的问题#
2. 连接的建立过程#
首先TCP服务器自身先建立相应的传输控制块,内容如下,并进入监听状态,等待TCP客户进程的连接请求:
然后TCP客户端发送TCP连接请求 SYN = 1, seq = x
,SYN设置为1代表这是一个连接请求,且TCP规定当SYN设置为1的报文段不能携带数据,并要消耗掉一个序号seq,示意图如下:
当TCP服务器接收到该请求后,会发送一个TCP连接请求的确认,SYN = 1, ACK = 1
代表这是对连接请求的确认,而 ack = x+1
表示对收到的请求报文的应答。同理,这个报文段也不能携带数据并要消耗掉一个序号seq。
最后TCP客户端接收到服务器的确认后还需再发送一个报文段进行应答,至TCP服务器接收到该报文后,连接正式建立:
三报文握手能否简化成两报文?
不能,一个简单的考量是:
- TCP客户发送连接请求后TCP服务器接收到时可以证明TCP客户端可以发送数据
- TCP服务器发送连接确认报文TCP客户接收到后可以证明服务器可以发送和接收数据
但如果不再发送一个额外的请求则无法证明客户端可以接受数据,因而无法保证连接的可靠性。
另外特殊的情况如下图所示:
3. 例题#
八、TCP连接的释放#
TCP通过“四报文挥手”释放连接。客户端和服务器都可以发起释放连接请求。
这里假设TCP客户端主动发起释放连接请求:
其中 FIN = 1
表示这是释放连接请求报文段,另外ACK = 1, ack = v
为对前面接收到的报文段进行确认,同理,释放连接请求也需要消耗掉一个序号seq = u
。
然后TCP服务器发送一个释放连接的确认报文,并通知应用进程,至此TCP客户到服务器的单向传输就关闭了:
此时TCP连接处于半关闭状态,即客户到服务器的连接断开,但服务器到客户的连接仍继续,服务器还可以继续向客户端发送数据:
当服务器要发送的数据也发送结束后,则服务器端也会发送一个释放服务器到客户端方向连接的请求,和客户端发送的释放请求的过程基本一致:
而客户端接收到释放请求时发送释放确认,并在等待2MSL后关闭连接,而服务器接收到该确认后直接关闭连接:
最后的这个等待时间是为了防止发生这种情况:
此外当连接过程中客户端出现故障时有这样的处理方案:
九、TCP报文的首部格式#
1. 字节流和TCP报文段的关系#
2. 格式与各部分的功能#
整体格式如下图所示:
- 源端口:占16比特,写入源端口号,用来标识发送该TCP报文段的应用进程。
- 目的端口:占16比特,写入目的端口号,用来标识接收该TCP报文段的应用进程。
- 序号seq:占32比特,取值范围[0, 232- 1],序号增加到最后一个后,下一个序号就又回到0。
指出本TCP报文段数据载荷的第一个字节的序号。
-
确认号ack:占32比特,取值范围[0, 232- 1],确认号增加到最后一个后,下一个确认号就又回到0。
指出期望收到对方下一个TCP报文段的数据载荷的第一个字节的序号,同时也是对之前收到的所有数据的确认。(累计确认)
若确认号=n,则表明到序号n-1为止的所有数据都已正确接收,期望接收序号为n的数据。 -
确认标志位ACK:取值为1时确认号字段才有效;取值为0时确认号字段无效。
TCP规定,在连接建立后所有传送的TCP报文段都必须把ACK置1。
- 数据偏移:占4比特, 并以4字节为单位。
用来指出TCP报文段的数据载荷部分的起始处距离TCP报文段的起始处有多远。
这个字段实际上是指出了TCP报文段的首部长度。
首部固定长度为20字节,因此数据偏移字段的最小值为(0101)2
首部最大长度为60字节,因此数据偏移字段的最大值为(1111)2
-
保留:占6比特,保留为今后使用,但目前应置为0。
-
窗口:占16比特,以字节为单位。指出发送本报文段的一方的接收窗口(cwnd)。
窗口值作为接收方让发送方设置其发送窗口的依据。
这是以接收方的接收能力来控制发送方的发送能力,称为流量控制。 -
校验和:占16比特,检查范围包括TCP报文段的首部和数据载荷两部分。
在计算校验和时,要在TCP报文段的前面加上12字节的伪首部。
几个标志位:
-
同步标志位SYN:在TCP连接建立时用来同步序号。
-
终止标志位FIN:用来释放TCP连接。
-
复位标志位RST:用来复位TCP连接。
当RST=1时,表明TCP连接出现了异常,必须释放连接,然后再重新建立连接。
RST置1还用来拒绝一个非法的报文段或拒绝打开一个TCP连接。 -
推送标志位PSH:接收方的TCP收到该标志位为1的报文段会尽快上交应用进程,而不必等到接收缓存都填满后再向上交付。
URG和紧急指针:
紧急标志位URG:取值为1时紧急指针字段有效;取值为0时紧急指针字段无效。
紧急指针:占16比特,以字节为单位,用来指明紧急数据的长度。
当发送方有紧急数据时,可将紧急数据插队到发送缓存的最前面,并立刻封装到一个TCP报文段中进行发送。紧急指针会指出本报文段数据载荷部分包含了多长的紧急数据,紧急数据之后是普通数据。
选项部分:
- 填充:由于选项的长度可变,因此使用填充来确保报文段首部能被4整除
(因为数据偏移字段,也就是首部长度字段,是以4字节为单位的)。
以上就是TCP报文首部各字段的详细含义。