后台开发核心技术(09):运输层总结

与上篇文章一样,还是重点关注因特网的运输层协议,即TCP、UDP运输层协议。

运输层协议

运输层协议是在端系统中实现而不是在路由器中实现,是直接服务于应用层,为应用层提供可靠传输的、面向连接的TCP或者不可靠传输的UDP。

在介绍运输层协议之前,先简单说一下IP协议(网络层协议),为主机之间提供了一种逻辑通信,IP服务模型是尽力而为交付服务,其实就是不可靠服务。

一、运输层服务的基本职责
UDP和TCP最基本的职责之一是:将两个端系统间的IP的交付服务扩展为运行在端系统上的两个进程之间的交付服务,就是为网络层和应用层中间提供服务的,这种服务是:将主机间交付扩展到进程间交付被称为运输层的多路复用与多路分解!!!(记住多路复用多路分解是运输层提供的服务。)
还有一个最基本的职责:差错检验!这两种职责是最低限度的运输层服务,也是UDP提供的仅有的两种服务。

二、多路复用与多路分解
总述:
我们知道套接字是进程间交互的门户,是网络数据传递的门户,现在我们考虑接收主机 怎样将一个到达的运输层报文段定向到适当的套接字? 这也就是多路复用和多路分解的功能。
为此目的,每个运输层报文段中有几个字段,在接收端,检查这些字段,表示出接受套接字,进而将报文段定向到该套接字。将运输层报文段中的数据交付到正确的套接字叫做 多路分解,在源主机从不同的套接字中收集数据块,并为每个数据块封装上首部信息从而生成报文段,然后传递到网络层,叫做 多路复用

实现方法:
源端口号目的端口号 来作为套接字的标识(唯一标识,有时会需要别的信息),在主机上为每个套接字分配一个端口号,当报文段到达主机时,运输层检查报文段中的目的端口号,并将其定向到响应的套接字,然后报文段中的数据通过套接字进入其所连接的进程。

UDP的多路复用与多路分解:
UDP的特点是一个端口号对应一个进程,对应一个socket,即 一对一对一 的关系,所以确定UDP的多路复用和多路分解非常简单——UDP的套接字是由一个二元组全面标识的,该 二元组 包含一个目的IP地址和一个目的端口号。服务端只需要知道从这个端口接收数据即可,其余的一概不管,然后UDP的源地址被提取出来作为服务端的目的地址返回消息。

TCP的多路复用与多路分解:
TCP是面向连接的、可靠的传输,是一对一的通讯,所以TCP套接字是由一个 四元组 来唯一标识的!这个四元组是:源端口号、目的端口号、源IP地址、目的IP地址。有了这四个标识,能够唯一确定客户端进程和服务端进程,即便来自同一个端口不同的两台客户机上,也是两个不同的套接字,同样,即便同一台机器上的两个客户端进程,由于发送请求的端口不同,也是两个不同的套接字。

强大的TCP连接怎么处理一个服务器连接多个客户端?
通常一个Web服务器主机会同时为很多客户端提供服务,是不是很疑惑一个Web服务器明明只开启了一个端口,怎么同时与很多客户端保持连接的呢?不是一一对应的吗?这个问题之前就困扰过我,我之前也写过这样的文章,这里简单描述一下,一个重要的信息是:连接套接字(注意不是监听套接字)与进程之间并非总是一一对应关系,一个服务器可以有很多连接套接字,以专门处理不同的客户,实现一对一,事实上,现在的高性能服务器通常只使用一个进程,但是为每一个新的客户连接创建一个新连接套接字的新线程,这个创建的新连接套接字就是在三次握手时创建的,注意看上一篇文章TCP网络编程中的细节。

三、无连接运输层详解——UDP
如前面所说,UDP除了差错检验和实现多路分解、复用,几乎没有对IP增加任何别的东西。应用层感觉像与IP直接打交道一样的裸奔感,哈哈~
DNS是通常使用UDP的应用层协议的例子,UDP有时因为其特点会更加适用于一些场合,其特点有:
1)关于发送什么数据以及合适发送很好控制:只要给UDP,UDP立马发出去,而TCP有时会受拥塞控制、流量控制的约束。
2)无需建立连接,连接需要很大的时延,三次握手四次挥手的过程。
3)无状态连接,不需要维护状态,TCP中的序列号和确认号是维护可靠传输的必要条件。
4)头部小,一般情况UDP头部8个字节,TCP头部20个字节。

UDP报文段结构
在这里插入图片描述
四、可靠数据传输原理——TCP
TCP提供可靠的数据传输,它依赖了很多基本原理,有差错检验、重传、累计确认、定时器、序号和确认号等。

面向连接的TCP——三次握手:
大家都知道TCP是从三次握手开始,这里用图表示会更直观一些:
在这里插入图片描述
三次握手就是三条箭头绿线,详细说一下,首先 客户端(发起请求的)调用connect发起建立连接的请求,此时随机生成一个序列号作为开始(以防止与上个套接字的序列号巧合一样),此时的ACK是服务端的随机生成的第一个序列号,这里不得不强调一下,我们的确认号是来自对方下一次要接收到的序列号,比如我已经接收到了30,我下一次应该接收31了,我就返回确认号31,这里看到 第二次握手 是服务器收到来自客户端的请求,然后发送一个自己收到的ACK和序列号,最后一次 是客户端响应从服务器收到的数据,一般第三次握手也可以捎带数据发送给服务器。

多播:由于TCP的点对点连接,所谓的“多播”——即在一次发送操作中从一个发送方数据传给多个接收方,在TCP中是不可能的。

发送缓存、接收缓存:建立了TCP连接,两个进程就可以相互发送数据了,客户进程通过套接字传递数据流,TCP套接字将这些数据引导到该连接的 发送缓里 (send buffer),发送缓存是发起三次握手期间设置的缓存之一,接下来TCP就会不时从缓存中取出一块数据(后面详细说)送到网络层发送,可以取出的数据数量受限于最大报文长度(MSS)。
当TCP从另一端收到报文段时,该报文段的数据就被放到了TCP连接的 接收缓存 中(涉及到同步、异步、阻塞、非阻塞),应用程序从接收缓存中读取数据。

TCP报文段结构
在这里插入图片描述
可以看到源端口号、目的端口号和UDP相同,被用来进行多路复用和多路分解的。另外检验和字段和UDP一样,除此之外TCP包含:
1)32为序号和确认号,被用来实现可靠数据传输。
2)16比特接收字段窗口:用于流量控制。
3)4比特首部长度字段:表示首部长度,这里以32比特为单位,可以得到4)比特最大表示15,在这里1532比特=60字节,所以首部最多表示60字节,一般情况下如上图,是325(行)=20字节。
5)6比特的标识字段(有未用):ACK表示确认字段值,RST、SYN、FIN用于连接的建立和拆除,在明确拥塞通告中使用CWR和ECE。

序号和确认号以及累计确认:
TCP报文段的序号和确认号是可靠传输的关键部分。序号建立在字节流上的而不是建立在报文段上的,一个报文段的序号是该报文段首字节的字节流编号。每一个序号被填写到响应的报文段首部中。确认号前面提了一下,一句话概括是: 主机A填充进报文段的确认号是主机A期望从主机B收到的下一字节的序号。 同时TCP的 累计确认机制 是接收端只确认期望收到的“下一字节的序号”,即便收到后面的报文段,仍然会等待“下一字节”的到来(或者超时重传等)。

超时重传与定时器
超时?什么叫超时呢,首先等待确认时间一定要超过平均往返时间(RTT)才合理,否则会造成不必要的重传。看一下估计往返时间:

EstimateRTT = 0.875*EstimateRTT + 0.125*SampleRTT

用SampleRTT去更新EstimateRTT,其中SampleRTT不选择重传的例子,因为其不具备典型性。
还定义了RTT偏差DevRTT以反映往返时间的波动。

DevRTT = 0.75*DevRTT + 0.25*(SampleRTT-EstimatedRTT)

我们设置超时时间的话,应该设置为比EstimatedRTT多一些余量,如果DevRTT波动大,余量就应该大一些,反之应该小一些。

TimeoutInterval = EstimatedRTT + 4 * DevRTT

定时器就是为最小未被确认的序列号计时用的,TCP里只采用一个计时器,而不是每个报文段都用计时器,这太耗费资源和时间了,第二个报文段在新的ACK超时时间发生前达到,第二个报文段就不会被重传。

同时一个有意思的情况是,如果提前收到了后面序号的确认报文,则这个序号中间的报文一定全部收到了,因为基于累计确认,如果没收到,接收方不会发送确认报文,可能中间的确认报文丢失掉了。

超时时间间隔加倍,每次TCP超时重传都会将下一次的超时时间间隔加倍,而不是通过EstimatedRTThe DevRTT推算得到。避免某个时刻网络拥塞。

快速重传
如果接收方接收到了大于所期望的序列号,它检测到了数据流中的一个间隔,就是说有报文段丢失,接受方会发送一个最后一个按序接收到的字节数据重复确认,比如我原来已经确认了序号100,我现在期待120到来,结果150到来了,我就返回一个100的确认,这叫做 冗余ACK ,同样如果一个接着一个的冗余ACK到来,达到3个冗余ACK ,就直接进行快速重传,已经将期待的报文段看作是丢失了。不用等超时就直接进行重传。

对TCP差错恢复做一个总结:
通过前面的超时重传,快速重传,确认号和序列号的检测,累计确认机制基本上已经实现了TCP的可靠数据连接,现在总结一下,TCP的差错恢复是回退N步还是选择重传?其实很灵活,是GBN和SR的混合体。
在缺失中间一个报文段的时候不会直接丢弃后面有效的报文段,而是选择缓存,这类似与选择重传,其累计确认机制又类似于回退N步,同时还有快速重传和累计确认带来的避免第一个报文段重传等巧妙之处,可谓非常成熟。

流量控制
问题背景:前面说过,TCP连接的每一侧主机都为该连接设置了接收缓存,当TCP接收到正确、有序的字节后就会将数据存放进入接收缓存,如果接收方读取速度慢而发送方发送过快,接收缓存就可能装满字节,而后续的字节就会溢出。
方法:所以TCP提供了流量控制,所以流量控制是一个与速度匹配的服务,即发送方的发送速率与接收方应用程序的读取速率相匹配。
实现: TCP通过让发送方维护一个接受窗口的变量来提供流量控制,就是接收方用接收窗口这个变量给发送方一个指示——该接收方还有多少可用的缓存空间,这就是TCP首部的接收窗口的作用。
特殊情况: 如果接收方最后一次接收数据后没有缓存空间了就发送给了发送方一个0,那么等接收方有空间了以后,发送方也不知道,怎么办呢?TCP规范中要求:当接收主机接收窗口为0时,发送主机继续发送只有一个数据字节的报文段,如果接收方清理了缓存就会给发送方一个缓存变量rwnd大于0的数。

拥塞控制
TCP拥塞机制相关理解:
由于IP不会向TCP提供任何网络拥塞的信息,所有TCP拥塞控制一定是建立在端到端的(主机的端到端),TCP如何控制拥塞的呢?——使用一个拥塞窗口的变量cwnd,它对一个TCP发送方能向网络中发送流量进行了速率限制,特别是,在一个发送方中未被确认的数据量不会超过cwnd和rwnd中的最小值(拥塞窗口和接收窗口)。
关于拥塞的指示TCP是以丢包事件作为标志(丢包产生要么是路由器缓冲区溢出,要么是超时或者三次冗余ACK),出现丢包现象就是TCP需要以cwnd进行拥塞控制时候。

下面是TCP拥塞控制算法:
1、慢启动
首先,cwnd的值以一个MSS开始,当收到确认ACK时就将cwnd的值翻番,1个MSS -> 2个MSS ->4个MSS…何时结束这种指数增长呢?第一种情况, 如果超时TCP发送方将cwnd设置为1,并重新开始慢启动,并且设置阈值ssthresh为原来cwnd/2。第二种情况就是当到达或者超过ssthresh阈值时,停止指数增长,结束慢启动,转移到拥塞避免阶段。第三种情况,检测到三个冗余ACK的情况,这时候TCP进行快速重传,并直接进入快速恢复的状态!

2、拥塞避免
进入了拥塞避免阶段,cwnd的值大概都是上次遇到拥塞时的一半,距离拥塞可能并不遥远,所以不翻倍增长,而采用了这种拥塞避免的保守方法:每个RTT只将cwnd的值增加一个MSS。但是何时停止增长呢? 情况一, 出现超时事件发生后,反应与慢启动中的一样,cwnd设置为1个MSS,ssthresh的值被更新为cwnd的一半。情况二,三次冗余ACK,这种情况相对来说不会那么严重,TCP将cwnd值减半(再加上冗余ACK数),ssthresh的值记录为cwnd值得一半,接下来进入快速恢复状态。

3、快速恢复
快速修复不是必须得构件,较新的版本中是这样的:ssthresh阈值被设置为当前cwnd的一半,cwnd拥塞窗口降低一点进入拥塞避免状态,具体降低多少,有种策略是原来的cwnd与现在的ssthresh中间值。

总结一下拥塞控制,如果丢包由3个冗余ACK产生,那么加性增、乘性减。
如果超时产生丢包,那么阈值设置为其一半,然后重新进行慢启动。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值