# 传输层
概念传输层的主要任务是向用户提供可靠的端对端服务,同名的传送报文。它向高层屏蔽了下层数据通信的细节,因而是计算机通信体系结构中最关键的一层。
相关协议:
# 理解
问题: 在 物理层 中讲到了 比特流 与 电信号 之间的切换,数据链路层 讲到了 数据帧 与 比特流 之间的转化,网络层 讲到了如何选择哪个路由、哪条路径转发 数据帧 到目标地。似乎没有涉及到可靠性,比如以下情况:
- 因为数据帧限制在了 1500 字节内,那超过该字节的文件就没法发送了吗?
- 问题 1 如果用分割数据包成数据帧、数据帧组合数据包的方法。那分割成的数据帧不再是一个整体,在网络层中可能会走不同的路线到达目标地,这意味着数据帧到达的顺序是乱的。这又该如何解决呢?
- 线路故障、路由表更新期间等因素都可能导致数据流中的一些包丢失。这种 丢包 现象如何解决?
- 发送端给已关机的接收端传输一个很大的文件,因为发送端是不知情,所以一直把文件发送完毕。这样必然会浪费带宽,又该如何解决?
回答: 如果学习过前面几层的话,应该能马上想到制定一个规范(TCP 协议),来解决以上问题。该协议解决对应问题的关键如下:
# 传输控制协议
传输控制协议是为了在不可靠的互联网络上提供可靠的端到端字节流而专门设计的一个传输协议。
# 结构
补充: 先简单的了解一下 TCP 结构,因为后面会围绕一些结构字段来讲解问题。
- 源端口号(16bit): 表示发送主机的端口号。
- 目标端口号(16bit): 表示接收主机的端口号。
- 序列号(32bit): 表示发送数据的位置。
- 确认应答号(32bit): 表示下一次应该收到的序列号,也可以表示目前接已经收到数据的位置(确认应答号减一)。后面会讲解到
- 数据偏移(32bit): 区分 TCP 的头部与真正数据的位置。因为后面有 长度可变 的 选项字段。
- 保留(4bit): 主要为了以后扩展使用,目前未使用上。默认为 0
- 控制位(8bit): 共 8 个控制符,每个占 1bit。如下:
- CWR: 拥塞窗口减少标志,用来表明它接收到了设置 ECE 标志的 TCP 包。并且,发送方收到消息之后,通过减小发送窗口的大小来降低发送速率。
- ECE: 用来在 TCP 三次握手时表明一个 TCP 端是具备 ECN 功能的。在数据传输过程中,它也用来表明接收到的 TCP 包的 IP 头部的 ECN 被设置为 11,即网络线路拥堵。
- URG: 紧急指针是否有效。如果 URG=1,表示某一位需要被优先处理。
- ACK: 确认应答号是否有效。如果 ACK=1,则表示应答号为有效。TCP 规定除了最初建立连接时的同步包之外该位必须设置为 1。
- PSH: 告诉对方收到该报文段后是否立即把数据推送给上层。如果 PSH=1,表示应当立即把数据提交给上层,而不是缓存起来。
- RST: 示是否重置连接。如果 RST=1,说明 TCP 连接出现了严重错误(如主机崩溃),必须释放连接,然后再重新建立连接。
- SYN: 在建立连接时使用,用来同步序号。当 SYN=1,ACK=0 时,表示这是一个请求建立连接的报文段;当 SYN=1,ACK=1 时,表示对方同意建立连接。SYN=1 时,说明这是一个请求建立连接或同意建立连接的报文。只有在前两次握手中 SYN 才为 1。
- FIN: 标记数据是否发送完毕。如果 FIN=1,表示数据已经发送完成,可以释放连接。
- 窗口大小(16bit): 能一次性拼接的数据大小。如果为 0,则表示可以发送窗口探测,以了解最新的窗口大小。
- 紧急指针(16bit): 只有在 URG=1 时有效。该字段的数值表示本报文段中紧急数据的指针。
- 选项(可变): 用于提高 TCP 的传输性能。
# 序列号与确认应答号
- 问题 1: 因为数据帧限制在了 1500 字节内,那超过该字节的文件就没法发送了吗?
- 问题 2: 问题 1 如果用分割数据包成数据帧、数据帧组合数据包的方法。那分割成的数据帧不再是一个整体,在网络层中可能会走不同的路线到达目标地,这意味着数据帧到达的顺序是乱的。这又该如何解决呢?
回答: 和物流一样,货物一车拉不下,那肯定分成几车来拉货物吖。将文件分批次进行传输,不过需要告诉接收方:这是从哪开始的数据包(序列号=x),然后就等待接收方回应。接收方收到数据包后,会通知发送方:下一个要从哪开始发送(确认应答号=x+1)。
思考: 发送一份数据包,就要继续等待对方响应,才能继续发送。如果端对端传输时间需要 10 秒种,那发送方需要等待 10*2=20 秒,才能发送下一份数据包;如果有 10 个数据包需要传输,就需要等 10 * 20 = 200 秒了。这样的效率难以让人接受,那又该如何解决?方案
# 重发
问题: 序列号与确认应答号 能解决相应的问题,但如果其中一方迟迟没有收到对方的 响应或数据(丢失或超时),哪岂不是会一直傻傻的等(很浪费资源)?😭 这该如何解决?(包含问题 3)
回答: 根据之前成功通信的时间来计算 “差不多该收到回应”的时间。如果超过这个时间,就当作对方收不到,重发一次数据。
补充: “差不多该收到回应”的时间,其实是两倍的最长报文段寿命(MSL)时间。因为一来一回,两个报文。
TIP
就像你微信约你朋友中午一点到万达看电影,但是到了一点半,你朋友还是没到,你脑子会猜想?
- 他没看到微信?
- 他在路上?
- 他没出门?
- 他有事情?
- ...
有很多种可能,你是无法确定是哪种情况的。过了很久很久,实在没回消息和没人来,你就会重新发微信。(我不知道这例子恰不恰当😆)
# 三次握手
对于问题 4: 可以发送数据包,来判断对方是否在线。就跟聊天一样,需要彼此回复对方来判断对方是否在线。
回答: 图中发送的三个数据包,其实就是三次握手的意思。过程如下:
- SYN=1 表示客户端与服务器建立新连接,需要开始同步。seq=x 表示 序列号 为随机数,对方应答的时候需要。
- ack=x+1 表示服务器收到客户端的要求 ,ACK=1 表示 ack 是有效的。于此同时,服务器也需要和客户端进行连接,参数与 过程 1 相似,SYN=1、 seq=y。
- 客户端也需要和 过程 2 一样回应服务器 ACK=1、ack=y+1,只不过不需要进行同步了,因为 过程 1 已经进行了。其实该过程可以携带数据了,携带数据的同时需要填入序列号 seq。
WARNING
SYN、ACK 为 控制位 。seq 为 序列号,ack 为 确认应答号。ACK 不是 ack !
补充: 其实可以看成双方互相连接的过程,只不过一些请求可以合并而已。如下图:
思考: 有连接(握手)过程,是否有断开(挥手)过程?四次挥手
# 四次挥手
回答: 有连接(握手)过程,必然有断开(挥手)过程。过程如下:
- ① 客户端说:“我没内容发给你了,我这边准备下线了 (FIN=1),我发一个随机数 (seq=u),你用这个回复我”
- ② 服务器收到客户端的消息后,便回应:“你要准备下线,我知道了 (ack=u+1,ACK=1)!不过你先等等,我这边好像还有内容没发完。等好了我再通知你。” 该回复也可以携带原本的数据,不过要设置确认应答号。如果还有剩余的数据没发,服务器会继续发送数据。
- ③ 客户端收到服务器的消息后,心理就在想:“那我再等等吧,看看还有什么内容发过来。”
- ④ 服务器发完剩余的数后,便通知客户端:“你刚刚不是说要下线吗?我内容发完了,你现在可以下线了 (ack=u+1,ACK=1)。因为我也没内容发了,所以我也准备下线了 (FIN=1)。我发一个随机数 (seq=u),你用这个回复我。”
- ⑤ 客户端收到服务器的消息后,便回应:“好的,你可以下线了 (ACK=1,ack=w+1)。我这也要下线了 (seq=u+1)。” 但是客户端突然想到:“万一他没有收到我的消息,该不会一直等着把?如果他没收到,自然会重发消息给我,我等 (2MSL) 时间看看会不会有。如果他没有重发,说明他收到了并下线了,我就可以下线了。”
- ⑥ 服务器收到客户端的消息后,便下线了。
注意: 如果其中的 ⑤、⑥ 步骤出现问题呢?假设:
- ⑤ 客户端告诉对方可以下线了,然后自己就下线了。刚好线路出现一些小问题,这条消息丢失了。服务器没收到消息,以为是自己发出的消息丢失了,便重发消息。但此时客户已经下线了,不会再回复服务器了。服务器的连接永远不会关闭 ⑥。
# 窗口控制
思考: 发送一份数据包,就要继续等待对方响应,才能继续发送。如果端对端传输时间需要 10 秒种,那发送方需要等待 10*2=20 秒,才能发送下一份数据包;如果有 10 个数据包需要传输,就需要等 10 * 20 = 200 秒了。这样的效率难以让人接受,那又该如何解决?
回答: 就像上传文件一样,很多应用上都是可以批量上传的,而不是一个一个手动上传。我们可以设置一个窗口范围,允许规定范围内的数据包发送。在这个范围内,不需要等确认应答。(窗口机制)
思考: 引入了 窗口概念 后的 重发机制 是否有变化或者是否可以进行优化?变化
TIP
窗口大小在握手期间初始化,随着后续的通信,会逐渐变大,然后趋于平稳。慢启动
# 窗口控制的重发
- 数据包丢失: 在没有窗口机制的时候是:发送方自己根据时间判断是否重发。而引入之后:接送方通过自己的应答来告诉发送方,只要有 3 次重复的应答,发送方就会进行重发。(又称 快速重发)
- 确认应答丢失: 在没有窗口机制的时候:确认应答丢失必然会导致重发。而引入之后:大大降低该情况的重发次数。比如:一次性发 6 个数据包,也意味着需要 6 个确认应答,但是前 5 个确认应答丢失都没有关系,只要第 6 个确认应答成功到达,就意味着前面 6 个数据包成功收到了。
# 用户数据报协议
人们为了更可靠的传输数据,而制定了 TCP 协议。握手、挥手、应答、重发等等步骤必然会影响到速度。但有时候人们又需要更快速度,比如:视屏通话,丢失一帧画面与延迟 10 秒,人们更加愿意选择前者。所以就有了 UDP 协议,相比之下就显得简单了。只需要将数据发送出去后,就不管了,即使丢包了,也不会重发。