软件开发速学计网:传输层

前言,要完整的学计网看这,韩立刚老师的课,绝对有趣,而我这是参考慕课网:编程必备基础中的网络篇,都是软件的,不是很全,但是对于搞软件的我觉得够了。有错请指出,谢谢

1. 概述

传输层中为应用层实体提供端到端的通信功能或者说管理端到端的通信,保证了数据包的顺序传送及数据的完整性。传输层中主要有两个重要的协议:TCP协议和UDP协议。

端到端是建立在点到点的基础上,点到点通信是指的是信道间的通信,而端到端指的是应用程序(进程)之间的通信。

在操作系统中也有进程的概念,那些进程中的通信,比如共享内存,UNIX域套接字是单机的,即仅限于一台主机内通信,不能跨网络。而网络通信,比如浏览器访问网页,需要通过网络去该网站的后台取得数据,这就是网络中进程间的通信。

常使用端口(Port)来标记不同的网络进程。端口(Port)使用16比特位表示(0~65535)。常见的网络进程端口有:(FTP:21)、(HTTP:80)、(HTTPS:443)、(DNS:53)等。

2. UDP协议

UDP(User Datagram Protocal,用户数据报协议):数据报是应用层传输过来的数据,UDP不会对该数据报做任何处理,会封装到协议里面然后传输出去。

在这里插入图片描述

2.1 UDP的组成

在这里插入图片描述

  • 源端口号:源机器使用的网络进程。
  • 目的端口号:目的机器使用的网络进程。
  • UDP长度:指的是UDP的首部和UDP数据的总长。首部长度固定为8字节。
  • UDP校验和:由发送端计算和存储,由接收端校验。解决数据正确性问题。

2.2 UDP的特点

  • UDP是无连接协议,即不需要建立连接即可传输数据。跟电话系统相反。
  • 正因为是无连接的协议,所以UDP不能保证可靠的交互数据,即无法保证传输的数据是否会丢失。
  • UDP是面向报文传输的,就是UDP数据报。(TCP就不是)
  • UDP没有拥塞控制。它不会感受到网络是否拥塞,所以总是发送数据。
  • UDP首部开销非常小,总共才8个字节。

在这里插入图片描述

3. TCP协议

TCP(Transmission Control Protocol,传输控制协议):是为了在不可靠的互联网络上提供可靠的端到端字节流而专门设计的一个传输协议。

3.1 TCP的特点

  • TCP是面向连接的协议,即通信时需要先建立连接。比如打电话。
  • TCP的一个连接有两端(端到端),而不考虑中间网段和节点。比如打电话的双方。
  • TCP提供可靠的传输服务
  • TCP协议提供全双工的通信。全双方就是双方可以同时进行对话(发送和接收)。
  • TCP是面向字节流的协议。字节流就是流入进程或流出进程的字节序列。把数据拆分成几段连续字节流来传输,在接收端把字节流组合起来。

在这里插入图片描述

3.2 TCP的组成(只看首部)

在这里插入图片描述

  • 序号(seq):因为数据会拆分成几段连续字节流后封装到TCP数据报,所以需要记录拆分后每个TCP数据报中的第一个字节的序列号(在原先未拆分前的位置),它的范围为:0~(2^32)-1,在TCP报文中一个字节一个序号。就比如 helloworld,这一串字节流,假设被拆分成了三个 TCP 报文段,第一个报文段携带了 hel,第二个报文段携带了 lowo,第三个报文段携带了 rld,这三个报文段不一定是按照顺序送到对端的,那么对端收到这三个段是如何确定他们的顺序的呢?此时序号的意义就体现在这里。参考:TCP的序号

(TCP至少有20个字节的首部)
在这里插入图片描述

  • 确认号(ack,与大写ACK不一样,看下面的TCP标记):期望收到的下一个数据报中数据的首字节序号,它的范围为:0~(2^32)-1。假设确认号为N,则表示N-1序号的数据都已经收到了。前面这句话更准确的表示意义是:假设接收方确认接收到了1到n-3、n-2、n-1字节,那么接收方就会返回一个确认号为n,表示n之前的字节都已经收到,现在期望接收第n个字节,即是对接收到的数据的最高序列号的确认。好好理解这个例子,比如传1,2,3,而接收方只接收了1,3,这时如果返回确认号4(表示4之前的都收到了)那就是出错了吧,所以接收方返回确认号一定为2。
  • 数据偏移:占4位(0~15),该字段的值是TCP首部(包括选项)长度除以4。表示传输数据偏移TCP首部的距离,主要是TCP选项导致的,因为TCP选项是可选,不知道内容是多少。数据偏移的范围:20字节(固定,即前5行,每行有4个字节,即4*5)到60字节(最大有15行包括固定部分和可选部分,每行4个字节,即15*4)
  • TCP标记:占6位,每位各有不同含义。(重要,需要记住)

在这里插入图片描述

  • 窗口(cwnd):占16位(0~(2^16)-1),指明对方可以发送的数据量,用于控制TCP连接中数据传输的速率。比如确认号为501,窗口为1000,则501~1501该范围的字节都可以接收。
  • 检验和:由发送端计算和存储,由接收端校验。解决数据正确性问题。
  • 紧急指针:当TCP标记中URG=1时,才启动。该指针指定紧急数据在报文的位置。

4. 可靠传输的基本原理

4.1 停止等待协议

如图:理想情况下的传输,消息1发送完,停止发送,等待确认1,类似消息2,3也是。
在这里插入图片描述

以下是三种出差错情况:

  1. 发送的消息在路上丢失了
    在这里插入图片描述
  2. 确认的消息在路上丢失了
    在这里插入图片描述
  3. 确认的消息很久才到
    在这里插入图片描述

其中,通过超时重传来保证可靠传输。

TCP有四个定时器,先学一种:重传计时器:每发送一个消息,都需要设置一个计时器,该计时器设置一个时间,如果信息在规定时间内还没收到接收方的确认消息,则可以重传。

停止等待协议是最简单的可靠传输协议,但对信道的利用效率不高。

4.2 ARQ协议

ARQ(Automatic Repeat reQuest,自动重传请求)。前面的停止等待协议,每次只能发送单个信息和确认效率低下,现在提出了ARQ协议来进行批量发送和确认,该协议有两个精髓:滑动窗口和累计确认

  • 滑动窗口:滑动窗口的大小意味着接收方还有多大的缓冲区可以用于接收数据,即接收窗口。发送方可以通过滑动窗口的大小来确定应该发送多少字节的数据,即发送窗口。接收窗口和发送窗口的大小可以不同。

现在,如图,发送方窗口内的序列号可以代表了那些已经被发送,但是还没有被确认的帧,或者是那些可以被发送的帧。现在发送方发送了第一个和第二个字节。
在这里插入图片描述
只要接收到确认消息,那么窗口会继续滑动,把还没进入窗口的字节加进来。
在这里插入图片描述

但是每一个报文都需要确认的话,确认消息的开销很大,所以提出了不需要对每个报文进行确认,采用累计确认方法。假设1到5发送,如果接收方收到了1到5,那么就直接返回5,表示下次接收时是第5个字节。
在这里插入图片描述
那么就把窗口滑动5格。
在这里插入图片描述

  • 累计确认:即是对接收到的数据的最高序列号的确认。好好理解这个例子,比如传1,2,3,而接收方只接收了1,3,这时如果返回确认号4(表示4之前的都收到了)那就是出错了吧,所以接收方是返回确认号一定为2。跟前面的确认号的意义一样。

有了累计确认就不用一个一个去确认。

所以连续ARQ协议比起停止等待协议的信道利用率更高,批量传输。

5. TCP协议的可靠传输

TCP的可靠传输基于ARQ协议,TCP的滑动窗口以字节为单位,以超时重传来保证可靠性,即接收方需要发送确认消息给发送方,如果一定时间内发送方没有收到确认消息,则认为消息没有到达接收方,重新发送数据。

如图:实际中的窗口大小可容纳上千个字节甚至更多。一个信息段可分成:已经确认的字节序号,允许发送的字节序号,不允许发送的字节序号。
在这里插入图片描述

下面的例子跟前面的例子一样,发送了四个字节,现在已经确认前两个字节已经接收,则窗口会滑动。
在这里插入图片描述

当然,如果可用窗口为0,而已发送的还未确认则不会滑动。
在这里插入图片描述

其实,发送端每发送一个报文段,就启动一个重传定时器并等待确认信息;接收端成功接收新数据后返回确认信息。若在定时器超时前数据未能被确认,TCP就认为报文段中的数据已丢失或损坏,需要对报文段中的数据重新组织和重传。这就是超时重传技术。注意:重传的次数是有限的,不可能无限重传。

在这里插入图片描述

如果每次都重传整个数据报那样就影响效率,因为有些已经确认收到了,所以可以选择重传,即选中某些字节范围重新传输,然后接收方将未按序号到达的数据包先放于缓冲区内,等待它前面的序号包到达后在组织。当然也可能出现全部都丢失的情况那就需要重发整个数据段。

具体请参考:TCP报文到达确认(ACK)机制
,里面说的未按序到达的例子中通常有两种处理方式:第一种就是对没有按序号到达的报文直接丢弃,这就需要重发整个数据报,第二种就是选择重传。他的例子:

发送方连续发送了每个报文中100个字节的TCP数据报,其序号分别是1,101,201,…,701。假如其它7个数据报都收到了,而201这个数据报没有收到,则接收端应当对1和101这两个数据报进行确认(累计确认),并将数据递交给相关的应用进程,301至701这5个数据报则应当放于缓冲区,等到201这个数据报到达后,然后按序将201至701这些数据报递交给相关应用进程,并对701数据报进行确认,确保了应用进程级的TCP数据的按序到达。

选择重传是利用TCP首部中的序号,首部使用TCP选项来存储选择所需重传的序号,因为重传并不是每次都要的。TCP选项最多可存储40个字节,也就是10个序号,但是这重传也太小了吧,实际上它是存储序号的边界。所以选择重传实际上是选择一段连续字节来重传。

(实际上每次发送的字节数是很多的,即滑动窗口很大,不是上面的例子中每次发送那么少)
在这里插入图片描述

6. TCP协议的流量控制

流量控制是TCP特有的功能。就是接收方希望发送方发送的数据慢一下,不要一直一直发送,接收方都接收不了太多,即让发送发的发送速率不要太快。流量控制是使用滑动窗口来实现的

结合TCP组成中首部的窗口,指明对方可以发送的数据量。
在这里插入图片描述

假设确认号是501,窗口为1000,则表示501~1501该范围的字节都可以接收。

正常情况下:
在这里插入图片描述

如图,当发送方收到接收方的窗口为0时,发送方一直等待接收方把窗口扩大以便传输数据,现在接收方扩大窗口,然后把窗口扩大的信息发送给发送方,此时在发送时却丢失了,而接收方也在等待发送方发送信息。此时双方都在互相等待出现了死锁。所以发送发需要一个计时器来询问接收方窗口是不是调大了,该定时器叫做计时器*。
在这里插入图片描述

坚持计时器:

  • 当接收到窗口为0的消息,则启动坚持计时器。
  • 坚持计时器每隔一段时间发送一个窗口探测报文,即去询问接收方窗口有没有调大。

7. TCP协议的拥塞控制

与UDP相反,TCP协议有拥塞控制,就是我们常说的塞车情况。

先来说说根源:在数据报传输时,经过很多设备,但经过的设备可能性能差距不一样或者速率不一样,这可能会造成网络传输的瓶颈(就好像在很低配置的主机运行需要高配置的3D游戏的那种情况)。也就是导致拥塞。

拥塞控制与流量控制:

  • 流量控制考虑的是点对点的通信量的控制,强调TCP连接双方的发送-接收速率
  • 拥塞控制考虑的是整个网络,是全局性的考虑,控制强调网络环境的状态

拥塞需要去判断,方法非常粗暴,就是:报文超时则认为可能是拥塞。因为有可能某些路由器处理不过来导致报文超时。当然插网线,自己网络有问题有可能导致报文超时。所以只需要知道报文超时是可以判断拥塞的一个方法

拥塞控制中会启动四个算法

  • 慢启动算法:由小到大逐渐增加发送数据量,每收到一个报文确认,就加一。增长是以指数增长。比如1,2,4,8,16这样。增长到慢启动阈值(ssthresh),然后就启动第二个算法。
  • 拥塞避免算法,维护一个拥塞窗口的变量,该变量大于慢启动阈值。只要网络不拥塞(报文不超时),就试探着拥塞窗口调大。比如前面假设16是慢启动阈值,则会慢慢调到17,18,19这样,直到发生拥塞。
  • 快重传算法:首先要求接收方每收到一个失序的报文段就立即发出重复确认(为的是使发送方及早的知道有报文段没有到达对方)而不要等到自己发送数据时才捎带确认。转载,老师只是讲了前面两个算法
  • 快恢复算法:当发送方连续收到三个重复确认时,就执行“乘法减小”算法,把ssthresh门限减半。但是接下去并不执行慢开始算法。考虑到如果网络出现拥塞的话就不会收到好几个重复的确认,所以发送方现在认为网络可能没有出现拥塞。所以此时不执行慢开始算法,而是将cwnd(窗口)设置为ssthresh的大小,然后执行拥塞避免算法。转载

如果拥塞发送了,就降低发送的数据量。

拥塞控制的实现:TCP连接的发送方可以感知网络环境的状态并做出反应。发送端使用慢启动算法、拥塞避免算法、快恢复算法、快重传算法。

在这里插入图片描述

8. TCP的三次握手建立连接

回顾前面的TCP标记,该标记中有三个需要在建立连接时使用的:ACK,SYN,FIN。

三次握手部分参考:TCP的三次握手与四次挥手(详解+动图):可以看看这个。需要知道的两点:TCP规定,SYN报文段(SYN=1的报文段)不能携带数据,但需要消耗掉一个序号;ACK报文段可以携带数据,但是如果不携带数据则不消耗序号。

(说接收方或服务器都可以,说发送方或客户端都可以)
三次握手:

  • 第一次握手:接收方是时刻保持监听状态来接收报文(LISTEN)。发送方主动的向接收方发送一个SYN报文,即TCP标记中SYN=1,表示这是一个连接请求的报文,并且带上该报文的序号(seq,假设值为x),然后等待回应,此时发送方进入同步已发送状态(SYNC-SENT)。
  • 第二次握手:接收方收到SYN报文,会被动地打开TCP连接,同时给发送方也发送一个SYN报文,并且TCP标记中ACK和SYN都为1,和序号(seq,假设值为y)、确认号(ack,则ack=x+1)。此时接收方进入同步已接收状态(SYNC-RCVD)
  • 第三次握手:发送方继续向接收方继续发送一个ACK报文,即TCP标记中ACK=1,和seq(seq=x+1)、ack(ack=y+1)。此时发送方就进入建立连接状态(ESTABLISHED),当接收方收到报文ACK后,也进入建立连接状态(ESTABLISHED)。至此就可以通信了。

在这里插入图片描述

面试常问的一个问题:为什么要有第三次握手? 前两个其实已经可以来建立连接了啊?

因为:来防止已经失效的连接请求报文又发送到对方导致想再建立一个连接,会引起错误。

解释:第一个报文发送后超时了,重新发送第二个报文。那么现在第一个报文就是属于失效的连接请求报文。
在这里插入图片描述
现在假设只有两次握手引发的后果:假设第一个报文发送后超时了(但假设未丢失,只是太慢了),所以会重新发送第二个相同报文。第二个报文很快到达接收方,建立连接。但是过一段时间,突然接收到了第一个报文,本来该报文已经是失效的,但是因为两次握手,又必须建立连接。一次通信居然连接了两个连接,这可能会导致错误和资源的浪费。
在这里插入图片描述
所以如果采用3次握手,那么失效的报文在第三次握手时,发送方并不会发送确认消息了(因为已经建立连接了)。而接收方又没有收到发送方的回应,则就不会再建立连接。

9. TCP的四次挥手释放连接

四次挥手也可叫四次释放或四次分手。

(TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。)

  • 第一次挥手:发送方主动发送连接释放FIN报文,即TCP标记中的FIN=1,并带上一个序号(假设seq=u),并停止发送数据。然后发送方就进入了连接结束的第一个等待状态(FIN-WAIT-1)
  • 第二次挥手:接收方被动收到FIN报文,发出ACK报文,该报文TCP标记中的ACK=1,并带上一个序号(假设seq=v)和确认好(ack=u+1)。此时接收方就处于关闭等待状态(CLOSE-WAIT),但接收方还是可以向发送方发送数据的,因为可能接收方有些数据还没发完,注意是发送方主动要释放连接的。所以发送方在收到ACK报文后还不能关闭连接,此时处于终止连接的第二个等待状态(FIN-WAIT-2)。
  • 第三次挥手:当接收方发送完数据后,接收方就发送一个FIN报文,即TCP标记中的FIN=1,并且ACK也为1,携带seq=w和ack=u+1。这是为了对第一次挥手的报文再进行确认。所以此时接收方进入了最后确认状态(LAST-ACK)。
  • 第四次挥手:当发送方接收到FIN报文后,也发出ACK报文,携带seq=u+1和ack=w+1。此时发送方会触发一个等待计时器(TIME-WAIT),等待一段时间确认没有问题后,发送方就进入关闭连接状态(CLOSE)。当接收方接收到ACK报文后也进入关闭连接状态(CLOSE)。

在这里插入图片描述

每次挥手都会有一个重传计时器。

MSL:Max Segment Lifetime,即最长报文段寿命。对于网络来说,MSL通常是2分钟。

等待计时器:等待2倍的MSL。连接会占用端口,只有等待计时器结束后才释放端口。建立在我们主动去释放连接的基础上。

典型问题:为什么要等待2MSL?

答:有两点。

  • 第一点:因为最后一次挥手发送的报文是没有确认的,所以确保发送方的ACK可以到达接收方。所以2MSL是报文可以在网络中存活的时间。如果接收方在2MSL时间内没有收到发送方的ACK报文,则认为接收方的第三次挥手是有问题的,需要重发第三次挥手的报文。
  • 第二点:确保当前连接的所有报文都已经过期了。因为最后一次报文都需要等待2MSL,那其他的报文一定超过了2MSL的时间了。防止出现防止已经失效的连接请求报文又发送到对方导致想再建立一个连接,会引起错误。(跟前面第三次握手的意义一样。)

10. 4个计时器(重要)

目前已经知道了3个计时器:重传计时器,坚持计时器,等待计时器。还有最后一个,即保活计时器。

10.1 重传计时器

每发送一个消息,都需要设置一个计时器,该计时器设置一个时间,如果信息在规定时间内还没收到接收方的确认消息,则重传。

每次重传都会再设置一个计时器。并且重传是有限次数的。

需要注意的是,发送端在超时定时器撤销之前,必须继续缓存已发送未确认的报文,直到发送端收到了来自接收端的确认。

10.2 坚持计时器

是使用滑动窗口进行流量控制的时候而设置的。这时需要理解“零窗口通知”的情况。

“零窗口通知”:接收端通过调整接收窗口的大小可以控制发送端的发送速度,当接收端把接收窗口调小时,那么发送端就会调小发送的流量。
这就可能产生一种情况,就是接收端的缓存区已经满了,这个时候接收端会给发送端发送一个“零窗口”的消息,表示说“当前我已经没有余力处理更多的数据了”,这就是“零窗口通知”的情况。

当出现这种情况的时候,双方都会陷入等待的状态,发送端等待接收端的窗口调大,接收端等待发送端发送的数据。当接收端窗口可以调大的时候,接收端会发送窗口调大的信息给发送端,但是这个消息是不可靠的,也即是这个消息可能会在传输中丢失,并且不会被感知到丢失和重传。

如果这个消息在发送过程中丢失的话,那么发送端和接收端就会进入死锁状态,因为接收端认为“我已经把窗口调大的消息发送出去了,发送端理应发送新的消息给我才对”,所以接收端会一直等待发送端的消息;而发送端因为没有收到窗口调大的消息,则认为“接收端还没有调大窗口,因此我不能发送”,发送端也会一直等待。
因此为了解决这个问题,当发送端收到窗口为零的消息之后,会启动一个坚持定时器来周期性主动的向接收方查询,以便发现窗口是否增大,这个就是坚持定时器的作用。

10.3 等待计时器

由主动关闭TCP连接的一方设置的,当主动关闭TCP连接的一方收到来自对方的FIN报文的时候(第三次挥手),则认为对方也可以关闭TCP连接,这个时候主动关闭TCP连接的一方发送一个消息确认的报文(第四次挥手),并启动这个时间等待计时器,这个计时器会等待2倍MSL的时间,MSL(Max Segment Lifetime),最大报文段寿命。

假设主动关闭方为A。
这个定时器主要是为了正确关闭一个TCP连接而考虑的,这主要是为了保证A在对最后一个FIN报文(第三次挥手)发送确认的报文可以到达B。

当A发出这个报文之后,就会启动2MSL计时器,注意,这个报文是有可能在网络传输过程中丢失的,如果B收不到这个确认,那么B会重新发送一次FIN报文,A会重新收到这个报文并重传一次最后的确认,并重新启动2MSL计时器,直到双方正常结束TCP连接。2MSL时间可以保证当B没有收到确认时,B可以再次发出FIN报文,并且A可以再次收到并重新发送确认,所以2MSL的时间可以保证连接正常结束。

10.4 保活计时器

为了保活TCP连接而设计的,保活计时器可以防止TCP连接的两端出现长时期的空闲,当一方出现状态变化或故障时,另一方没有察觉的情况。

设想连接双方在建立连接后,只传输了一些数据,然后就都保持静默了,双方也都没有关闭连接(这种情况经常存在),如果这个时候其中一方已经故障,那么这个连接将会永远被打开,如果被连接的一方是服务端的话,那将浪费很多服务端的资源。

因此为了解决这个问题,服务端一般都会设置一个保活定时器,每次收到对方的数据则重置这个定时器,如果定时器超时,服务端则发送探测报文段,探测客户端是否还在线,如果没有收到响应的话,那么则认为客户端已经断开连接了,因此服务端也会终止这个连接。

11. 套接字

套接字的格式就是:{IP:Port},即一个IP地址和一个端口号。可以在网络中指定一台主机中的具体进程是什么。因为IP地址是网络中唯一的,端口号是主机进程唯一的。

套接字(Socket),表示TCP连接的一端。通过套接字可以进行数据发送或接收。

TCP是两端连接的,那么就需要两个套接字。即:TCP={Socket1:Socket2}={{IP:Port},{IP:Port}}。

在java中有一个网络编程概念,使用Socket类,该类说的就是这个套接字。所以一般网络编程是通过传输层进行传输和接收数据。

发布了69 篇原创文章 · 获赞 65 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_41800884/article/details/104500344