运输层
运输层
应用层的作用是为程序提供一种相互通信的方式,那么运输层就是提供应用层协议提供一种服务,建立一条进程间逻辑通信的链路,而为运输层提供服务的是网络层的协议,为运行在不同主机上进程提供一条逻辑同学链路。
运输层的多路复用、多路分解
多路复用:
从不同的soket中接收数据块将数据块封装上首部信息,生成报文段后传递到网络层中。
多路分解:
将运输层报文段中的数据交付给正常的套接字。
UDP多路复用和多路分解
UDP无连接运输层协议,向目的进程发送分组时不需要提前建立链接,也不需要知道目的主机是否存在该目的端口的进程,UDP只需要将数据块从socket中取出,然后再首部加上运输层首部就可以将分组向外发送。
当目的主机接收到分组时,不管分组来自哪里,只要将数据块放入端口号对应的socket中就好了。
TCP多路复用和分解
TCP有链接运输层协议,再传送分组时需要提前简历链接,不同的链接有各自的socket的,TCP从socket取出数据块后封装上运输层首然后将分组推向目的地。
当目的进程接收到分组后,取出分组的运输层首部,通过识别源ip地址,端口号;目的ip地址,端口号;将分组送到对应的socket中。
UDP
为应用层提供运输服务中最简单的运输层协议,它仅仅提供简单的多路复用和分解的功能和少量的差错检测。
UDP的优点:
- 无线建立链接就可以发送分组,减少建立链接的开销。
- 分组的开销小,因为UDP并没有提供特别多的服务,所以首部行需要的字段就少了,UDP首部就小了。
UDP的缺点:
UDP没有拥塞控制功能,如果链路拥挤,UDP还持续往链路中推送大量的分组,这些分组不仅没法到达目的主机,也会导致链路持续崩溃,丢包率过高的问题。
可靠传输协议原理
理解可靠传输协议之前,我们先了解下没有可靠传输协议时,分组再链路中传输会遇到什么问题。
- 分组受损
- 分组丢失
面对第一个问题,我们先假定分组不会丢失。
我们能做的就是,当接收方接收到报文,发现这个报文再链路中被损坏了(由报文首部提供检错码检测),那么接收方就向发送方,发送一个NAK报文,说明这接收到的报文坏了,重新发送一次。如果报文没损坏,那么就发送ACK报文。
但是又遇到了一个问题,就是确认报文也是会损坏的,我们可以很容易的通过检错码检测确认报文是否损坏,但是很难进行纠错,此时我们就要考虑是否重发分组了。
如果我们要重发分组,这时候我们可以通过为分组引入序号,因为要让接收方确认该分组是否是冗余分组,这个分组之前是否接收过,确认报文中应该包含哪个分组的序号。
我们也可以通过让发送方接收冗余的ACK取缔NAK报文,具体的就是如果接收方接收分组时损坏的,那么我们就发送上一个分组的ACK,此时发送方就接收到了两个上一个分组ACK,它就知道此时的分组没有被正确接收,那么就重发现在的分组。
发送方的自动机:
接收方的自动机:
现在我们引入第二个问题, 分组丢失的问题。我们现在要考虑分组再链路上会丢失时,我们应当怎么处理。
这时我们可以为发送方添加一个定时器,规定发送分组的确认报文没有再固定时间内到达发送方,那么发送方直接重传分组。当发送方接收到确认报文时,我们将定时器停止重置,当再发送一个分组时,我们再将定时器打开。
发送方的自动机:
流水线可靠传输协议
以上针对如何设计一个可靠传输协议时根据停等模型设计的,就是说,当发送方发送一个分组,那么他就要停下来等待该分组的确认报文到达,才继续发送下一个分组。
停等模型会导致信道的利用率低的问题,这与操作系统的单道程序设计系统问题一致,如果单道系统中进程阻塞,那么CPU就空闲出来照成CPU资源的浪费。信道的利用率也是如此。
为了提高CPU的采用流水线化的传输分组模式,也就是说,不再等待分组的确认报文,直接发送下一条分组。
这会带来新的问题,就是如果发送了N个分组,其中N个分组中有个分组丢失了或者受损了,那么怎么进行重传。
退回N步
由于TCP的流量控制和拥塞控制,我们流水化发送分组的个数也是有限制的,例如N个,如果已发送队列中已经又N个发送但为确认的分组,那么发送方就不能继续向接收方发送分组,否者发送方就可以继续发送分组。
在回退N步协议中,接收方不缓存失序的分组,也就是如果接收方期待接收第N个分组,但是先到的确实第N+1个分组,那么接收方将向发送方发送一个期待接收第N个分组的确认报文,这时候发送方将重新发送第N个分组和N后面发送过的分组。具体的细节看自动机。
发送方自动机:
接收方自动机:
选择重传
选择重传机制用于解决回退N步照成不必要的重发问题。在选择重传中,接收方需要缓存失序的分组,待失序分组之前的分组到达后,一起传递给上层。
发送方的动作:
1.从上层接收数据,检查是否有可用序号,如果有,就发送这个分组,没有就将数据还给上层.
2.超时:每个分组要有自己的逻辑计时器.
3.收到ACK如果ACK等于send_base,那么我们更新send_base的值到最近一个没被确认的分组序号,此时空闲出的序号就可以发
1.中未能发送的分组。
接收方的动作:
1.收到一个分组,如果这个分组没有被接收过那么缓存这个分组,并向发送方返回该分组的ACK,如果这个分组序号等于rcv_base 那么将之前缓存的与rcv_base连续的分组以上传递给上层。并将接收窗口向前移动。
2.序号在[rcv_base-N, rcv_base-1]内的分组被正确接收,也要给发送方发送一个ACK,不管这个分组之前有没有被接收过。
3.其他情况忽略。
第2.是必须的,因为之前接收方接收到该分组但是确认报文在链路中丢失了,那么发送方不得不重新发送这个分组,如果接收方不做回应,那么发送方的滑动窗口将无法向前移动。接收方也将无法接收到任何分组。
TCP
TCP是运输层的可靠传输协议,是有链接,全双工的。TCP中的一些原理与之前说的可靠传输协议原理部分类似。
例如:
TCP中的缓存大小 ——》 滑动窗口的大小
TCP的重传 ——》选择重传和退回N步重穿
再说TCP之前,我们先明确一些概念;
- MSS(最大报文长度):报文段中数据部分的大小
- MTU(最大传输帧):由物理链路决定。
报文段的MSS由MTU设置,例如MTU规定最大传输帧是1500KB,那么MSS就是1460KB,因为TCP+IP的报文首部由40KB的大小。
TCP向数据块中添加的首部只包含源端口号和目的端口号,而确认一个分组到底要送往哪个Socket还需要源ip和目的ip,但是这并不是运输层做的事情,这是网络层做的事。
TCP的工作方式:
-
TCP从上层拿数据块,如果发送缓存还有空间,则为这个数据库封上首部发送给网络层
-
TCP从网络层中拿到报文段放在接收缓存中,等待上层应用来缓存中取数据。
TCP的报文段格式
序号和确认号
序号和确认号是最重要的,是实现可靠传输协议的基础,如前面的可靠传输协议原理所说的那样。
发送方
可以通过接收方返回的报文段中的确认号序号发送下一个报文,可以通过确认号是否需要重发分组,还可以根据确认号更新发送缓存的大小。
接收方
接收方可以根据序号判断是否是冗余分组,也可以更具序号发送请求中间分组的报文。
在TCP中
序号:为报文段中数据字节流的首字节为序号.
确认号:接收方期望从发送方接收的下一个字节.
例如:
一个大小为3KB的数据,MTU只有41KB,则MSS只有1KB.
那么第一个报文的序号就是1,第一个报文的确认号报文就是2.
第二个报文的序号就是2,第二个报文的确认好就是3.
标志位
- RST,SYN,FIN 用于TCP连接的管理
- CWR,ECE 用于拥塞控制
- PSH 立即提交上层应用
- URG 设置为紧急报文段
TCP中提供积累确认
这点很重要,因为之后对TCP一些操作的理解有帮助。
接收方向发送方发送的确认号是最早未被确认的序号,例如说,发送方发送了1,2,3,4个报文,接收方先接收到了1和3,那么下一个确认号的报文是2而不是4。只有2被确认了4还没被确认才发送4。
TCP提供可靠的数据传输原理
如之前缩缩,一个可靠传输协议需要确保报文丢失和报文受损的解决方法,TCP的解决方法和之前讨论过的并没有什么太大区别。
例如,TCP也使用超时机制来预防报文丢失的事件,使用重发来解决报文丢失和报文受损事件。
- 当定时器超时时,发送方重新发送数据,并重启定时器。
- 当报文接收到确认报文时,如果确认号 > SendBase的话,那么更新SendBase = y;因为TCP为却早未被确认的序号,接收方发送的序号也是最早还没被接收的确认号。如果当前还有没有被确认的报文,那么重启定时器。
其他情况
- 接收方接收重传分组,根据序号判断是否时接收过的分组的数据,如果是则丢弃数据。
- 接收方接收两个相邻的序号的分组,同时回发两个分组的确认号,若第一个确认报文段丢失,第二个到底,那么将确认这两个分组被接收即使第一个分组丢失了,因为发送方只发送最早未被接受的分组。
TCP的超时加倍重传
这一点与之前介绍的原理不太一致,但是也可以理解,因为,当确认报文在规定时间内没有按时到达发送方,那么说明物理链路过于拥挤,导致分组丢失或者晚到达,那么我们可以通过加长定时,减少重传的频率以及给确认报文在链路中更多的时间。这样是有意义的。
快速重传
这也很好理解,当一个分组连续超时,触发超时传输的时间可能是会与的长,会增加端到端的延时。我们需要一个机制在超时时间触发之前重发分组,通过冗余ACK的方式来判断。为了理解冗余ACK是怎么产生的,我们需要看一下接收方是如果发送ACK的。
- 当所期望的ACK到达时,之前接收的序号都被确认时——》延时ACK,500ms,如果下一个按序报文没有到达,那么发送一个ACK
- 当所期望的报文段按序到达,另外一个报文段等待发送ACK,也就是1.情况的超时版本,就是说,接收方不用未每一个接收报文发送一个确认号,因为TCP提供积累确认,只要发送一个确认号就可以确认之前所有的已发送分组。
- 比期望序号大的报文段到达(失序了),立即发送要给ACK,最早未被接收的确认号。
- 到达的报文段填充了间隔,那么发送下一个填充间隔的ACK,下一个最早为被接收的确认号。
所以为什么未产生冗余ACK,就是报文段失序到达, 第一个间隔的报文一致未到,就接收方就一致发送第一个间隔的ACK。
TCP的重传方法
选择重传和回退N步的结合。
流量控制
TCP提供流量控制和拥塞控制,但是这两者并不相同。
- 流量控制:速率匹配服务,消除缓存溢出的可能性,因为接受方接收到数据之后,并不是直接将数据交付上层,而是放在接收缓存中,应用也不会立即从接收缓存中取数据,发送方若发送数据的速率大于接受方应用取数据的速度,那么会导致数据溢出,接受方无法正确的接收到数据。
- 拥塞控制:因为网络IP拥塞而降低发送速率。
流量控制的实现
发送方维护一个接收窗口的变量,该变量秒速接受发还有多少空间可以缓存分组。
- LBRead:进程从缓存中读取数据流的最后一个字节
- LBRcvd:从网络中接受并放入缓存中的最后一个字节编号。
则接收窗口的变量值为RcvBuffer - (LBRead - LBRcvd)。接受方将这个值插入到ACK报文中,发送方可以根据这个值调整窗口的大小。
发送方的发送窗口大小 = (LBSent - LBACKed) <= rwnd接收窗口的大小。
当接受方的接收缓存满了的时候,发送方依旧发送一个确认接收窗口大小的报文来获取接收窗口的大小,避免发送方永久阻塞。
TCP链接管理
建立链接
- 发送方向接收方发送一个特殊的报文段以请求建立链接,该报文中标志字段SYN被设置为1,同时选择一个随机的初始序号最为client-isn的值。
- 接收方接收到请求连接的报文,并未该链接分配缓存和变量,并向发送方发送允许连接的特色报文,SYN=1,随机选择一个接收方的初始序号为server-isn的值,并且设置确认号 = client-isn + 1。
- 发送方接收到允许建立链接的报文,并未这TCP设置缓存和变量,并向接收方发送包含数据的报文段,SYN=0,确认号 = server-isn + 1,序号 = 接收方发送的确认号,数据。
关闭链接
假设发起关闭链接请求的是接收方;
那么接收方先发送一个请求拆链接的特殊报文,FIN = 1。当发送方接收到拆链接的请求报文后,发送一个该报文的确认报文,然后发送方也发送一个请求拆链接的特殊报文,FIN = 1,接收方发送一个确认报文给发送方。然后链接拆除。
思考
- 为什么建立链接要三次握手?
因为两次搞不定, 并不是收到了确认建立链接报文的就一定建立好了链接, 例如接收方返回了一个确认建立链接报文后,电脑关机了,TCP并未被建立,那么就需要在发送一个报文段,来确认链接是否建立完成,诺接收方接受了分组那么就可以确认TCP是建立完成的了。
这和请人帮忙是一个道理,对方口头答应并不是说明一定会帮忙,若对方突然有事情没法帮忙的情况也可能存在。
- 为什么断开链接要四次挥手?
这个也很好理解,因为例如接收方发起断开链接的报文时,接收方还有分组没有被确认接收,一定要等到发送方把所有分组都确认接收之后,才可以断开链接。所以拆链接之前为了避免另一方还有事务未完成之前拆链接就需要两方都共同确认拆连接时才可以真的进行拆链接的操作。
拥塞控制原理
再说明拥塞控制的方法之前,我们要了解拥塞出现的原因,以及拥塞带来的代价。
链路拥塞的原因是路由器两端链路容量的不一致性导致的。大容量链路意味着吞吐量更高,发送速率也更高。而小容量意味着发送速率更小。所以当大容量链路的发送方向链路以饱和的发送速率向链路中发送数据,到了路由器,由于接收方的链路容量小,路由器没法一下子转发所有的到达分组,那么只能暂时存放再路由器的缓存中。当缓存满时,再到达路由器的分组将被丢弃照成分组丢失。
拥塞的代价:
- 再链路高延时的情况下,分组未到达接收方,发送方就判断该分组超时重发该分组,那么链路中就存在两个一样的分组。此时分组重传就浪费链路的带宽去转发一个没用的分组。
- 当链路拥塞时,发送方还要将链路的一部分带宽用来重发因为路由器缓存满造成分组丢失的问题。
- 当一分组再链路中被丢弃时,从分组的发送方到被丢弃的位置的这一段链路的资源都被浪费了。
拥塞控制的方法
-
端到端:就是TCP通过报文段丢失的状况来猜测链路的拥塞情况,不过确认报文段丢失的情况封为两种不同的情况进行处理。
- 计时器超时
- 3个冗余ACK
-
网络辅助:路由器向发送方提供链路的状态信息。
- 通过网络路由器发送给发送方
- 通过发送方给接收方发送分组中更新字段,表明链路拥塞。
TCP的拥塞控制
TCP发送方维护一个拥塞窗口变量(cwnd)来进行对发送速率的控制。
发送方中的未确认的数据(LBsent-LBAcked)不能大于接收窗口和拥塞窗口中的最小值。这是因为:
-
当接收窗口的是最小值时,说明链路中不拥挤,但是接收方的应用接收数据太慢了,小于到达的数据,所以要进行流量控制。
-
当拥塞窗口是最小值时,说明发送方和接收方的速率差不多,但是链路中出现了拥塞,报文段丢失要进行拥塞控制。
TCP的拥塞检测方法:
- 计时器超时
- 3个冗余ACK
TCP对给定拥塞窗口控制发送速率的原则:
- 丢失报文段就意味着拥塞。
- 先前确认报文到达时,增加发送速率,因为拥塞可能解决了。
- 带宽检测,就是发送方不断的试探链路的底线(拥塞),当发生拥塞时,降低发送速率,再提供发送速率,反复如此。
TCP拥塞控制算法
慢启动
让TCP由一个较慢的发送速率快速成长到一个差不多快的发送速率。
给cwnd设置为一个mss,此时链路的容量可能大于发送方的发送速率,当发送方接收到一个ACK时,为每一个ACK增加一个MSS,直到拥塞出现.
降低发送速率的两种方案:
1.当遇到因为超时指示的丢包时,发送方将ssthreash设置为cwnd/2.
这里穿插一点,当cwnd >= ssthreash时,慢启动状态装变为拥塞避免的状态,降低cwnd的增长速率.
2.当遇到因为冗余ACK指示的丢包时,发送方将ssthreash设置为cwnd/2,并且cwnd设置为ssthreash+3MSS,对于这3MSS拥 塞避免会解释
拥塞避免
当慢启动时,cwnd增长到一定程度时,我们应该要避免这种野蛮成长的方式,如果继续这样的话,拥塞将很快到来。因此当慢启动状态到达一定程度上时,即cwnd >= ssthreash时,进入拥塞避免。
再拥塞避免阶段,TCP发送速率的增长从只要由一个ACK到达就为cwnd增加一个MSS,到一个RTT中有一个ACK到达(因为一个RTT中可能到达多个ACK),就为cwnd增加一个MSS,从时间和增加的空间上限制了增长的速率。
拥塞避免的降低发送速率方案和慢启动一致。
现在来解释下为什么当遇到因为冗余ACK指示的丢包时,cwnd并没有像超时那般变化严重。并且还要+3MSS。
这是因为当冗余ACK到达时,链路可能并不是很拥塞,只是那么一下,出现报文丢失的哪个报文运气不好遇到了突然的拥塞情况,因此冗余ACK指示的报文丢失并没有超时来的那么严重,那么我们就可以放宽条件啦。如果链路真的拥塞严重的话那么也不到到达3个ACK。
至于+3MSS是因为使得测量结果更好,为每个到达的ACK增加一个MSS。至于为什么慢启动和拥塞避免都是+3,因为从本质上来说,慢启动和拥塞避免都是通过接收一个ACK来增加发送速率,那么这冗余的3个ACK可能是3个RTT时分别到达,也可能是其他情况。遵循更严苛的增加条件。
快速恢复
对每个冗余的ACK增加一个MSS,
1.当到达一个新的从未接受过的ACK时,说明缺失的分组已经被接收方成功接收,那么从快速恢复转变为拥塞避免状态cwnd = ssthreash。
2.当出现超时时,发生的动作和慢启动发生的超时动作一致。