对于应用层来说,他就是一个可靠传输的底层支持服务;而运输层底层采用了网络层的不可靠传输。虽然在网络层甚至数据链路层就可以使用协议来保证数据传输的可靠性,但这样网络的设计会更加复杂、效率会随之降低。把数据传输的可靠性保证放在运输层,会更加合适。
可靠传输原理的重点总结一下有:滑动窗口、超时重传、累积确认、选择确认、连续ARQ。
停止等待协议
要实现可靠传输,最简便的方法就是:我发送一个数据包给你,然后你跟我回复收到,我继续发送下一个数据包。传输模型如下:
这种“一来一去”的方法来保证传输可靠就是停止等待协议(stop-and-wait)。不知道还记不记得前面TCP首部有一个ack字段,当他设置为1的时候,表示这个报文是一个确认收到报文。然后再来考虑一种情况:丢包。网络环境不可靠,导致每一次发送的数据包可能会丢失,如果机器A发送了数据包丢失了,那么机器B永远接收不到数据,机器A永远在等待。解决这个问题的方法是:超时重传。当机器A发出一个数据包时便开始计时,时间到还没收到确认回复,就可以认为是发生了丢包,便再次发送,也就是重传。但重传会导致另一种问题:如果原先的数据包并没有丢失,只是在网络中待的时间比较久,这个时候机器B会受到两个数据包,那么机器B是如何辨别这两个数据包是属于同一份数据还是不同的数据?这就需要前面讲过的方法:给数据字节进行编号。这样接收方就可以根据数据的字节编号,得出这些数据是接下来的数据,还是重传的数据。在TCP首部有两个字段:序号和确认号,他们表示发送方数据第一个字节的编号,和接收方期待的下一份数据的第一个字节的编号。前面讲到TCP是面向字节流,但是他并不是一个字节一个字节地发送,而是一次截取一整段。截取的长度受多种因素影响,如缓存区的数据大小、数据链路层限制的帧大小等。
连续ARQ协议
停止等待协议已经可以满足可靠传输了,但有一个致命缺点:效率太低。发送方发送一个数据包之后便进入等待,这个期间并没有干任何事,浪费了资源。解决的方法是:连续发送数据包。模型如下:和停止等待最大的不同就是,他会源源不断地发送,接收方源源不断收到数据之后,逐一进行确认回复。这样便极大地提高了效率。但同样,带来了一些额外的问题:发送是否可以无限发送直到把缓冲区所有数据发送完?不可以。因为需要考虑接收方缓冲区以及读取数据的能力。如果发送太快导致接收方无法接受,那么只是会频繁进行重传,浪费了网络资源。所以发送方发送数据的范围,需要考虑到接收方缓冲区的情况。这就是TCP的流量控制 。解决方法是:滑动窗口。基本模型如下:在TCP的首部有一个窗口大小字段,他表示接收方的剩余缓冲区大小,让发送方可以调整自己的发送窗口大小。通过滑动窗口,就可以实现TCP的流量控制,不至于发送太快,导致太多的数据丢失。连续ARQ带来的第二个问题是:网络中充斥着和发送数据包一样数据量的确认回复报文,因为每一个发送数据包,必须得有一个确认回复。提高网络效率的方法是:累积确认。接收方不需要逐个进行回复,而是累积到一定量的数据包之后,告诉发送方,在此数据包之前的数据全都收到。例如,收到 1234,接收方只需要告诉发送方我收到4了,那么发送方就知道1234都收到了。第三个问题是:如何处理丢包情况。在停止等待协议中很简单,直接一个超时重传就解决了。但,连续ARQ中不太一样。例如:接收方收到了 123 567,六个字节,编号为4的字节丢失了。按照累积确认的思路,只能发送3的确认回复,567都必须丢掉,因为发送方会进行重传。这就是GBN(go-back-n) 思路。但是我们会发现,只需要重传4即可,这样不是很浪费资源,所以就有了:选择确认SACK。在TCP报文的选项字段,可以设置已经收到的报文段,每一个报文段需要两个边界来进行确定。这样发送方,就可以根据这个选项字段只重传丢失的数据了。可靠传输小结
到这里关于TCP的可靠传输原理就已经介绍的差不多。最后进行一个小结:
通过连续ARQ协议与发送-确认回复模式来保证每一个数据包都到达接收方
通过给字节编号的方法,来标记每一个数据是属于重传还是新的数据
通过超时重传的方式,来解决数据包在网络中丢失的问题
通过滑动窗口来实现流量控制
通过累积确认+选择确认的方法来提高确认回复与重传的效率
当然,这只是可靠传输的冰山一角,感兴趣可以再深入去研究。
拥塞控制
拥塞控制考虑的是另外一个问题:避免网络过分拥挤导致丢包严重,网络效率降低。高速公路同一时间可通行的汽车数量是一定的,当节假日时,就会发生严重的堵车。在TCP中,数据包超时,会进行重传,也就是会进来更多的汽车,这时候更堵,最后导致的结果就是:丢包-重传-丢包-重传。最后整个网络瘫痪了。这里的拥塞控制和前面的流量控制不是一个东西,流量控制是拥塞控制的手段:为了避免拥塞,必须对流量进行控制。拥塞控制目的是:限制每个主机的发送的数据量,避免网络拥塞效率下降。就像广州等地,限制车牌号出行是一个道理。不然大家都堵在路上,谁都别想走。拥塞控制的解决方法是流量控制,流量控制的实现是滑动窗口,所以拥塞控制最终也是通过限制发送方的滑动窗口大小来限制流量。当然,拥塞控制的手段不只是流量控制,导致拥塞的因素有:路由器缓存、带宽、处理器处理速度等等。提升硬件能力(把4车道改成8车道)是其中一个方法,但毕竟硬件提升是有瓶颈的,没办法不断提升,还是需要从tcp本身来增加算法,解决拥塞。拥塞控制的重点有4个:慢开始、快恢复、快重传、拥塞避免。这里依旧献祭出大学老师的ppt图片:
Y轴表示的是发送方窗口大小,X轴表示的是发送的轮次(不是字节编号)。最开始的时候,会把窗口设置一个较小的值,然后每轮变为原来的两倍。这是慢开始。
当窗口值到达ssthresh值,这个值是需要通过实时网络情况设置的一个窗口限制值,开始进入拥塞避免,每轮把窗口值提升1,慢慢试探网络的底线。
如果发生了数据超时,表示极可能发生了拥塞,然后回到慢开始,重复上面的步骤。
如果收到三个相同的确认回复,表示现在网络的情况不太好,把ssthresh的值设置为原来的一半,继续拥塞避免。这部分称为快恢复。
如果收到丢包信息,应该尽快把丢失的包重传一次,这是快重传。
当然,窗口的最终上限是不能无限上涨的,他不能超过接收方的缓存区大小。
除此之外,还可以让路由器在缓存即将满的时候,告知发送方我快满了,而不是等到出现了超时再进行处理,这是主动队列管理AQM。此外还有很多方法,但是上面的算法是重点。面向连接
这一小节讲的就是无人不晓的TCP三次握手与四次挥手这些,经过前面的内容,这一小节其实已经很好理解。TCP是面向连接的,那连接是什么?这里的连接并不是实实在在的连接,而是通信双方彼此之间的一个记录。TCP是一个全双工通信,也就是可以互相发送数据,所以双方都需要记录对方的信息。根据前面的可靠传输原理,TCP通信双方需要为对方准备一个接收缓冲区可以接收对方的数据、记住对方的socket知道怎么发送数据、记住对方的缓冲区来调整自己的窗口大小等等,这些记录,就是一个连接。在运输层小节中讲到,运输层双方通信的地址是采用socket来定义的,TCP也不例外。TCP的每一个连接只能有两个对象,也就是两个socket,而不能有三个。所以socket的定义需要源IP、源端口号、目标IP、目标端口号四个关键因素,才不会发生混乱。假如TCP和UDP一样只采用目标IP+目标端口号来定义socket,那么就会出现多个发送方同时发送到同一个目标socket的情况。这个时候TCP无法区分这些数据是否来自不同的发送方,就会导致出现错误。
既然是连接,就有两个关键要点:建立连接、断开连接。
建立连接
建立连接的目的就是交换彼此的信息,然后记住对方的信息。所以双方都需要发送彼此的信息给对方:但前面的可靠传输原理告诉我们,数据在网络中传输是不可靠的,需要对方给予我们一个确认回复,才可以保证消息正确到达。如下图:机器B的确认收到和机器B信息可以进行合并,减少次数;而且发送机器B给机器A本身就代表了机器B已经收到了消息,所以最后的示例图是:步骤如下:
机器A发送syn包向机器B请求建立TCP连接,并附加上自身的接收缓冲区信息等,机器A进入SYN_SEND状态,表示请求已经发送正在等待回复;
机器B收到请求之后,根据机器A的信息记录下来,并创建自身的接收缓存区,向机器A发送syn+ack的合成包,同时自身进入SYN_RECV状态,表示已经准备好了,等待机器A 的回复就可以向A发送数据;
机器A收到回复之后记录机器B 的信息,发送ack信息,自身进入ESTABLISHED状态,表示已经完全准备好了,可以进行发送和接收;
机器B收到ACK数据之后,进入ESTABLISHED状态。
断开连接
1.机器A发送完数据之后,向机器B请求断开连接,自身进入FIN_WAIT_1状态,表示数据发送完成且已经发送FIN包(FIN标志位为1);2.机器B收到FIN包之后,回复ack包表示已经收到,但此时机器B可能还有数据没发送完成,自身进入CLOSE_WAIT状态,表示对方已发送完成且请求关闭连接,自身发送完成之后可以关闭连接;3.机器B数据发送完成之后,发送FIN包给机器B ,自身进入LAST_ACK状态,表示等待一个ACK包即可关闭连接;4.机器A收到FIN包之后,知道机器B也发送完成了,回复一个ACK包,并进入TIME_WAIT状态TIME_WAIT状态比较特殊。当机器A收到机器B的FIN包时,理想状态下,确实是可以直接关闭连接了;但是:如果此时机器A马上关闭连接,会导致数据不完整、机器B无法释放连接等问题。所以此时机器A需要等待2个报文生存最大时长,确保网络中没有任何遗留报文了,再关闭连接5.最后,机器A等待两个报文存活最大时长之后,机器B 接收到ACK报文之后,均关闭连接,进入CLASED状态双方之间4次互相发送报文来断开连接的过程,就是四次挥手。现在,对于为什么握手是三次挥手是四次、一定要三次/四次吗、为什么要停留2msl再关闭连接等等这些问题,就都解决了。UDP协议
运输层协议除了TCP,还有大名鼎鼎的UDP。如果说TCP凭借他完善稳定的功能独树一帜,那UDP就是精简主义乱拳打死老师傅。UDP只实现了运输层最少的功能:进程间通信。对于应用层传下来的数据,UDP只是附加一个首部就直接交给网络层了。UDP的头部非常简单,只有三部分:所以UDP的功能也只有两个:校验数据报是否发生错误、区分不同的进程通信。但,TCP的功能虽然多,但同时也是要付出相对应的代价。例如面向连接的特性,在建立和断开连接的时候会有开销;拥塞控制的特性,会限制传输的上限等等。下面来罗列一下UDP的优缺点:UDP的缺点
UDP的优点
效率更快;不需要建立连接以及拥塞控制
连接更多的客户;没有连接状态,不需要为每个客户创建缓存等
分组首部字节少,开销小;TCP首部固定首部是20字节,而UDP只有8字节;更小的首部意味着更大比例的数据部分
在一些需要高效率允许可限度误差的场景下可以使用。如直播场景,并不需要保证每个数据包都完整到达,允许一定的丢包率,这个时候TCP的可靠特性反而成为了累赘;精简的UDP更高的效率是更加适合的选择
可以进行广播;UDP并不是面向连接的,所以可以同时对多个进程进行发送报文
UDP适用场景
UDP适用于对传输模型需要应用层高度自定义、允许出现丢包、需要高效率的场景、需要广播;例如其他补充
分块传输
我们可以发现,运输层在传输数据的时候,并不是把整个数据包加个首部直接发送过去,而是会拆分成多个报文分开发送;那他这样做原因是什么?有读者可能会想到:数据链路层限制了数据长度只能有1460。那数据链路层为什么要这么限制?他的本质原因就是:网络是不稳定的。如果报文太长,那么极有可能在传输一般的时候突然中断了,这个时候就要整个数据重传,效率就降低了。把数据拆分成多个数据报,那么当某个数据报丢失,只需要重传该数据报即可。那是不是拆分得越细越好?报文中数据字段长度太低,会使得首部的占比太大,这样首部就会成为网络传输最大的负担了。例如1000字节,每个报文首部是40字节,如果拆分成10个报文,那么只需要传输400字节的首部;而如果拆分成1000个,那么需要传输40000字节的首部,效率就极大地降低了。路由转换
先看下图:
可以看出来,使用路由转发的好处是:提高网络的容错率,本质原因依旧是网络是不稳定的 。即使坏掉几个路由器,网络依旧畅通。但是如果坏掉路由器6那就直接导致主机A和主机B无法通信,所以要避免这种核心路由器的存在。使用路由的好处还有:分流。如果一条线路太拥堵,可以从别的路线进行传输,提高效率。粘包与拆包
在面向字节流那一小节讲过,TCP不懂这些数据流的意义,他只知道从应用层拿到数据流,切割成一份份报文,然后发送给目标对象。而如果应用层传输下来的是两个数据包,那么极有可能出现这种情况:应用层需要向目标进程发送两份数据,一份音频,一份文本
TCP只知道接收到一个流,并把流拆分成4段进行发送
中间第二个报文的数据就出现两个文件的数据混在一起,这就是粘包
目标进程应用层在接收到数据之后,需要把这些数据拆分成正确的两个文件,就是拆包
粘包与拆包都是应用层需要解决的问题,可以在每个文件的最后附加上一些特殊的字节,如换行符;或者控制每个报文只包含一个文件的数据,不足的用0补充等等。恶意攻击
TCP的面向连接特点可能会被恶意的人利用,对服务器进行攻击。前面我们知道,当我们向一个主机发送syn包请求创建连接时,服务器会为我们创建缓冲区等,然后向我们返回syn+ack报文;如果我们伪造IP和端口,向一个服务器进行海量的请求,会使得服务器创建了大量的创建一半的TCP连接,使得其无法正常响应用户的请求,导致服务器瘫痪。解决的方法可以有限制IP的创建连接数、让创建一半的tcp连接在更短的时间内自行关闭、延缓接收缓冲区内存的分配等等。长连接
我们向服务器的每一次请求都需要创建一个TCP连接,服务器返回数据之后就会关闭连接;如果在短时间内有大量的请求,那么频繁创建TCP连接关闭TCP连接是一个很浪费资源的行为。所以我们可以让TCP连接不要关闭,在这个期间进行请求,提高效率。需要注意长连接维持时间、创建条件等,避免被恶意利用创建大量的长连接,消耗殆尽服务器的资源。最后
以前学习的时候觉得这些东西好像没什么卵用,貌似就是用来考试的。事实上,在没应用到的时候,对这些知识很难有更深层次的认知,例如现在我看上面的总结,很多只是表面上的认知,不知道他背后代表的真正含义。但当我学习的更加广泛、深入,会对这些知识有越来越深刻的认识。有那么几个瞬间觉得:哦原来那个东西是这样运用,那个东西是这样的啊,原来学了是真的有用。现在可能学了之后没有什么感觉,但是当用到或者学到相关的应用时,会有一个顿悟感,会瞬间收获很多。
来源:https://juejin.cn/user/3931509313252552/postse