传输层
向它的用户(应用层的进程)提供高效的、可靠的和成本有效的数据传输服务
相对于网络层而言,传输层更加注重可靠的传输服务。
网络层也有定义“负责网络包的传输”,但网络层更加关注的是不同网络之间的包传输,比如分组算法和路由选择最佳路径、寻址等。
所以说传输层更像是“专注的”用于研究传输的过程的层级,在传输层中往往理解为传输的实体都是“固定的”,传输路径也是“固定的”。在这一章节当中,将会揭示传输层使用什么方式保证可靠连接,以及用途与做法。
原语 | 发出的包 | 含义 |
---|---|---|
LISTEN | 无 | 阻塞,直到某个进程试图与之连接 |
CONNECT | CONNECTION REQ | 主动尝试建立一个连接 |
SEND | DATA | 发送信息 |
RECEIVE | 无 | 阻塞,直到到达一个DATA包 |
DISCONNECT | DISCONNECTION REQ | 请求释放连接 |
一个简单的传输服务原语
原语 | 含义 |
---|---|
SOCKET | 创建一个新通信端点 |
BIND | 将套接字与一个本地地址关联 |
LISTEN | 声明愿意接受连接;给出队列长度 |
ACCEPT | 被动创建一个入境连接 |
CONNECT | 主动创建一个连接 |
SEND | 通过连接发送一些数据 |
RECEIVE | 从连接上接收一些数据 |
CLOSE | 释放连接 |
TCP套接字原语
寻址
计算机A上的应用程序a想访问计算机B上的应用程序b,那么他在发出CONNECT请求的时候,必须要知道程序b关联在计算机B上的端口,也叫做TSAP(传输服务访问点),并将自己与计算机A上某个端口关联。
找到b在B上的TSAP号,就是寻址的过程。
端口映射器存储了应用程序与端口的对应关系,程序a想要知道程序b的端口,那么先于端口映射器建立连接,返回到b的端口的时候再重新建立连接。
- 新的服务(应用程序)创建时需要向端口映射器注册
- 端口映射器的TSAP必须是广为人知的
可靠性连接/断开
某些情况下网络必须做到完全可靠,例如银行汇款、短信传输等,就算并不完全可靠,包可能发生丢失,也需要至少在发生错误的时候进行警告。
三次握手
- 同步初始序列号
- 避免打开历史连接请求
过程:
客户端发送SYN j
服务端回应SYN k,ACK j+1
客户端回应ACK k+1
什么叫“同步初始序列号呢”,首先序列号是按照字节流/数据包排列的,比如第一个发送出去的数据包为1,第二个就是2,接收端知道这样的规律之后就可以分辨认出是否发生错误,在发生错误之后再根据ACK机制回应发送方错误发生了。三次握手就保证经过一次交流之后双方都知道了必须的序列号规律
传输的时候有可能发生丢包现象,也有可能发生延迟现象,想象一下如果第一次握手发出的数据包延迟到达,触发了客户端的重传,此时服务端会接收两个时间不同的数据包。三次握手规定服务端需要接收客户端的回复才开启服务,所以对他来说这两次并无所谓。但试想如果服务端直接开启服务,但开启的这一份服务对客户端来说已经是无效的,客户端一直在等待第二次连接的回应,那么服务端就一直像忘记关闭的电灯泡一样销毁资源。此外,如果服务端一收到SYN就开启服务,那么他接收到这两次重传的SYN之后可以会直接建立两个连接,其中一个永远也接收不到消息,这样就造成了资源浪费。
总而言之三次握手遵循的就是“在得到对方已经接收到我的信息之前不会开启服务”这样一个谨慎了对策。
四次挥手
《计算机网络》一书举了一个很巧妙的例子,设想有俩军交战,蓝军从南方和北方将白军包围,现在的设定是白军可以战胜任何一边的蓝军,但两边蓝军同时进攻能战胜白军。
在可靠连接建立之前消息发送都是不可靠的,也就是说南北方蓝军派出的传信兵随时都可能被白军捕获,试想南边的部队成功发出“明天进攻”的消息,但北方的部队的回应传信兵被白军捕获,这个时候南边部队是不能随意出动的,因为“消息是否成功到达”这个消息对南边部队来说是未知的。
这个例子理论上是没有解决方案的,因为就算我们拥有三次握手协议,甚至四次五次六次无限次,最后一个消息发出方始终是不知道自己发出的消息是否被转达到位,于是始终会犹豫行动(现在终于知道抗战时期电报的重要性了)。
连接建立的时候只需要三次握手,因为三次握手之后,尽管第三次的消息的传达对于客户端来说是未知的,但其实对于传输的影响不大。试想一下客户端开启服务开始传输数据,而此时服务端由于未接收到第三次握手所以无法开启服务,自然也不会对客户端回应,此时客户端超时,自然也就了解到连接建立失败了,所以连接阶段只要保证服务端不会无限等待下去就行。
服务端真的会无限等待吗?其实TCP设立了保活计时器来避免这样的意外情况发生,比如连接已经建立之后服务端却突然故障。保活计时器会对服务端接收到的消息计时,如果一定时间内(一遍为两小时)没接收新信息则会强制断开。
而断开连接的时候比连接建立时要多想一层,因为断开连接时需要保证所有数据都传输到位,而不会因为某一方贸然断开连接而产生大量数据丢失。
过程:
客户端在发送完最后一个数据后接着发送一个释放请求,并停止发送数组FIN = 1,seq = u
服务端收到请求后立马发送一个确认ACK = 1,seq = v,ack = u + 1
服务端等待自己发送完所有数据,在最后一个数据尾接上释放请求FIN = 1,ACK = 1,seq = w,ack = u + 1
客户端接收到请求,发送确认消息ACK = 1,ack = w + 1,于是服务端释放,客户端在等待2 MSL(Maximum Segment Lifetime,最大报文存活时间)后也释放资源。
客户端总是比服务端晚释放,等待2MSL的原因是客户端需要确保服务端成功接收到ACK,并等待服务端有可能的重传,如果这段时间客户端收到了重传信息,客户端会重置2MSL的时间;其次,2MSL时间为了考虑最坏情况的发生,既服务端的超时时长
其实三次握手本身就是四次握手,只不过“累积确认”机制使确认帧和序列号帧同时传出,所以合为一次握手。此外四次握手将确认帧和请求帧分开,其实就是保证自己的数据也完全传出,这个时候如果服务端本身就完成了所有数据的传出,那么确认帧和请求帧就可以合并为一次挥手。
可靠性传输
不错(无比特差错)、不丢(不丢包)、不乱(包按顺序到达)
总览:
- 流量控制
- 拥塞控制
- 差错控制
滑动窗口
数据链路层中的滑动窗口和传输层中的滑动窗口很像,只不过传输层传输的是报文段或数据包,而数据链路层中传输的是帧。
滑动窗口协议建立在接收方与发送方各自维护的缓冲区上,缓冲区最初是针对等停协议来建立的,因为数据传输的时候完全等待确认帧到来之后再发送下一个帧是完全不现实的。
那么就来详细规定一下滑动窗口:
- 接收方维护一个缓冲区,窗口值等于可以继续接收字节流的大小,维护一系列数据包基序号,检查缓冲区中的数据包序号是否与基序号相匹配。
- 发送方维护一个缓冲区,窗口值等于可以继续发送字节流的大小,维护一系列已发送但未收到确认的包列表。
- 窗口大小通常以数据包的数量或以字节数来表示。较大的窗口大小可以提高传输效率,但也会增加缓冲区的需求和延迟。较小的窗口大小可以减少缓冲区的需求和延迟,但会降低传输效率。
- 两个缓冲区的作用使得字节流根据区域分成了四类:
状态 | 描述 |
---|---|
4 | 发送方尚未发送数据包,接收方未做好准备来接收数据 |
3 | 发送方尚未发送数据包,接收方已做好准备来接收数据 |
2 | 发送方已发送数据包,但尚未收到接收方的确认 |
1 | 发送方已发送数据包,并已收到接收方的确认 |
如果运行正常,字节流的状态会由2变为1,由3变为2,看起来就好像在向上滑动。
滑动窗口允许同时发送多个包,提高了利用率,控制滑动窗口的大小以控制数据传输的拥塞情况,也可以通过滑动窗口实现包的选择性重传,这些就是滑动窗口协议的优势
流量控制
流量控制的目的主要是防止发送方速率过快使接收方过载,有停止-等待和滑动窗口控制两者方式,通过控制滑动窗口大小,就能方便地进行流量控制。
接收窗口会时不时向发送方通知自己还剩多少空间够缓存,这段空间就叫做“通知窗口”,通知窗口为0的时候会发送零窗口通告,直到非0为止。同样,为了防止非零窗口通告丢失,发送方会维护一个定时器用于在等待期间时不时主动查询通知窗口大小。
此外,还有Clark算法(设置每次非零通告发出的最小等待时间)与Nagle算法(空间存到一定量再发送非零通告)也有助于流量控制。
拥塞控制
流量控制注重于点对点局部控制,拥塞控制注重于网络报文全局控制
拥塞就是指比如发送方发送速率过快导致网络资源(带宽、缓冲区)无法容纳流量数据的现象,容易导致据包的丢失、延迟增加和网络性能下降。主要控制发送方速率来实现。
拥塞窗口是发送方缓冲区中的一个参数,拥塞控制基本上就是通过调整这样一个参数来实现的。思路可以从两个方面来进行,那就是拥塞避免与拥塞恢复,分别对应拥塞控制的慢开始与快重传。
- 慢开始——设置拥塞窗口为一个较小的值——指数增长——到达阈值时线性增长(拥塞避免)——出现超时(发生拥堵),设置阈值为超时窗口的一半——重新慢开始
- 快重传:基于慢开始的算法,试想若接收窗口接收到一系列包中存在丢失情况,此时由于等待包更新导致接收窗口一直拥塞。快重传就是当遇到这种情况时立即发送多个冗余确认,使得发送方不需要等待计数器就立马发送丢失的包。报文丢失的情况下很容易发送拥塞,所以在快重传的基础上将拥塞窗口设置为收到三个冗余重传时拥塞窗口的一半,此时的拥塞窗口成为新的慢开始阈值。
比如M1、M2、M4,此时接收方收到M4的时候还是会重传M2的ACK,包括M5、M6冗余重传3次,这就是冗余重传
差错控制
分为检错重发法、前向纠错法、反馈校验法
TCP
TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、有序的、基于字节流的传输层通信协议。
比如连接时需要三次握手,释放时需要三次挥手,这个前文都介绍过。
TCP是面向字节流的传输协议,即1比特为一组传输数据。其段(一分组)结构如下所示:
端口:寻址到发送/接收端口的应用进程
序号:即序列号,本帧数据数列号
确认号:即ACK确认机制接收方期望接收到的序列号
TCP头长度:即用来直观显示TCP头长度的(20~40字节),占四位,会随着选项长度改变
标志:
- CWR:为1时即告诉发送方拥塞了,需要减少拥塞窗口
- ECE:为1说明发送方可以进行显式拥塞通知机制
- URG:需要发送紧急数据是为1,与紧急指针配合使用
- ACK:为1说明确认号合法,为0说明无确认信息
- PSH:为1时说明本段不需要等待缓冲区填满,尽快发出
- SYN:为1时申请建立连接,可以说建立连接时发送一个SYN请求
- FIN:为1时申请释放连接,即发送方没有数据发送了
接收窗口:用于存储接收方缓存的大小量,即还能存多少数据,方便告诉发送方进行流量控制
校验和:与差错检测相关,比如汉明码
紧急指针:指向一个报文段紧急数据位置(偏移量)
选项长度:TPC传输过程中有一些用于功能或者性能优化的选项(选择确认、时间戳等),选项长度就是用来指明选项选择情况的长度(可变)
填充:末尾添加额外字节,用于整个报文段达到一定长度或者对其需求,也可能为了安全性处理。
UDP报文结构较为简单,可以参考UDP协议报文结构_udp报文结构-CSDN博客
TCP中的计时器
重传计时器、持续计时器、保活计时器
重传计时器
很常提起的一个计时器,比如发出一个段的时候就会立即启用,被确认的时候就会停止计时,超时的时候就会重发,这就是典型的超时重传。
持续计时器
上文提到过接收窗口为0的情况,如果零窗口通告丢失的,则发送方和接收方就会发生死锁。那里提到的计时器就是持续计时器,当发送方接收到零窗口通告时会设定一个计数器,比如等待60s进行一次窗口探测。
保活计时器
也提到过,就是若某一方发生致命错误崩溃,那么另一方就长时间接收不到消息但仍然处于活跃状态。保活计时器就是通过及时检测长时间收不到消息就会启用调查,若发现另一方不回应则主动直接关闭连接。
总结
- 回退N和选择重传协议属于差错恢复的两个基本方法,即滑动窗口协议、
- TCP实现可靠性传输的方式有:序列号和确认应答、超时重传、滑动窗口、拥塞控制,UDP想实现可靠性传输就往这方面靠
- 传输层面向的是不同主机之间不同进程(应用程序)之间的传输,网络层面向不同主机提供通信服务(选择网络路径)
- UDP特点:面向无连接、尽最大可能交付, 面向报文, 没有拥塞控制, 支持单播、多播、广播的交互通信。
- TCP特点:面向连接 的,提供可靠交付,有 流量控制,拥塞控制,提供 全双工通信,面向 字节流,每一条 TCP 连接只能是 点对点 的(一对一)的传输层通信协议。
- 自动重传协议除了滑动窗口的两个基本方法,还包括停等式协议
图片来自网络侵删